unit RaGSQLite;

interface

uses
  DB,
  DBCommon,
  Dialogs,
  Classes,
  Windows,
  SysUtils,
  Variants,
  RaSqliteRout;
const
  SQLiteVersion     = 'Rarnu SQLite V1.0';

  MaxBuf            = 30000;            // max stringbuffer for record (length) (excluding blob's)
  SQLITE_OK         = 0;                // Successful result */
  SQLITE_ERROR      = 1;                // SQL error or missing database */
  SQLITE_INTERNAL   = 2;                // An internal logic error in SQLite */
  SQLITE_PERM       = 3;                // Access permission denied */
  SQLITE_ABORT      = 4;                // Callback routine requested an abort */
  SQLITE_BUSY       = 5;                // The database file is locked */
  SQLITE_LOCKED     = 6;                // A table in the database is locked */
  SQLITE_NOMEM      = 7;                // A malloc() failed */
  SQLITE_READONLY   = 8;                // Attempt to write a readonly database */
  SQLITE_INTERRUPT  = 9;                // Operation terminated by sqlite_interrupt() */
  SQLITE_IOERR      = 10;               // Some kind of disk I/O error occurred */
  SQLITE_CORRUPT    = 11;               // The database disk image is malformed */
  SQLITE_NOTFOUND   = 12;               // (Internal Only) Table or record not found */
  SQLITE_FULL       = 13;               // Insertion failed because database is full */
  SQLITE_CANTOPEN   = 14;               // Unable to open the database file */
  SQLITE_PROTOCOL   = 15;               // Database lock protocol error */
  SQLITE_EMPTY      = 16;               // (Internal Only) Database table is empty */
  SQLITE_SCHEMA     = 17;               // The database schema changed */
  SQLITE_TOOBIG     = 18;               // Too much data for one row of a table */
  SQLITE_CONSTRAINT = 19;               // Abort due to contraint violation */
  SQLITE_MISMATCH   = 20;               // Data type mismatch */
  SQLITE_MISUSE     = 21;               // Library used incorrectly */
  SQLITE_NOLFS      = 22;               // Uses OS features not supported on host */
  SQLITE_AUTH       = 23;               // Authorization denied */
  SQLITE_FORMAT     = 24;               // Auxiliary database format error
  SQLITE_RANGE      = 25;               // 2nd parameter to sqlite3_bind out of range
  SQLITE_NOTADB     = 26;               // File opened that is not a database file
  SQLITE_ROW        = 100;              // sqlite_step() has another row ready */
  SQLITE_DONE       = 101;              // sqlite_step() has finished executing */

  SQLITE_CREATE_INDEX = 1;              // Index Name      Table Name      */
  SQLITE_CREATE_TABLE = 2;              // Table Name      NULL            */
  SQLITE_CREATE_TEMP_INDEX = 3;         // Index Name      Table Name      */
  SQLITE_CREATE_TEMP_TABLE = 4;         // Table Name      NULL            */
  SQLITE_CREATE_TEMP_TRIGGER = 5;       // Trigger Name    Table Name      */
  SQLITE_CREATE_TEMP_VIEW = 6;          // View Name       NULL            */
  SQLITE_CREATE_TRIGGER = 7;            // Trigger Name    Table Name      */
  SQLITE_CREATE_VIEW = 8;               // View Name       NULL            */
  SQLITE_DELETE     = 9;                // Table Name      NULL            */
  SQLITE_DROP_INDEX = 10;               // Index Name      Table Name      */
  SQLITE_DROP_TABLE = 11;               // Table Name      NULL            */
  SQLITE_DROP_TEMP_INDEX = 12;          // Index Name      Table Name      */
  SQLITE_DROP_TEMP_TABLE = 13;          // Table Name      NULL            */
  SQLITE_DROP_TEMP_TRIGGER = 14;        // Trigger Name    Table Name      */
  SQLITE_DROP_TEMP_VIEW = 15;           // View Name       NULL            */
  SQLITE_DROP_TRIGGER = 16;             // Trigger Name    Table Name      */
  SQLITE_DROP_VIEW  = 17;               // View Name       NULL            */
  SQLITE_INSERT     = 18;               // Table Name      NULL            */
  SQLITE_PRAGMA     = 19;               // Pragma Name     1st arg or NULL */
  SQLITE_READ       = 20;               // Table Name      Column Name     */
  SQLITE_SELECT     = 21;               // NULL            NULL            */
  SQLITE_TRANSACTION = 22;              // NULL            NULL            */
  SQLITE_UPDATE     = 23;               // Table Name      Column Name     */
  SQLITE_ATTACH     = 24;               // Filename        NULL            */
  SQLITE_DETACH     = 25;               // Database Name   NULL            */

  SQLITE_DENY       = 1;                // Abort the SQL statement with an error */
  SQLITE_IGNORE     = 2;                // Don't allow access, but don't generate an error */

  Crlf              : string = #13#10;
  Q                 = '''';

type
  pInteger = ^integer;
  pPointer = ^Pointer;
  pSmallInt = ^smallint;
  pFloat = ^extended;
  pBoolean = ^boolean;


  TConvertBuffer = array[1..255] of char;

  TSQLite3_Callback = function(UserData: Pointer; ColumnCount: Integer; ColumnValues, ColumnNames: PPointer): Integer; cdecl;
  TSQLiteExecCallback = function(Sender: TObject; Columns: integer; ColumnValues: Pointer; ColumnNames: Pointer): integer of object; cdecl;
  TSQLiteBusyCallback = function(Sender: TObject; ObjectName: PAnsiChar; BusyCount: integer): integer of object; cdecl;
  TOnData = procedure(Sender: TObject; Columns: integer; ColumnNames, ColumnValues: string) of object;
  TOnBusy = procedure(Sender: TObject; ObjectName: string; BusyCount: integer; var Cancel: boolean) of object;
  TOnQueryComplete = procedure(Sender: TObject) of object;
  TRaSQLiteNotifyEvent = procedure(Sender: TObject) of object;

  TRaSQLiteField = class
  public
    FieldNumber: integer;
    FieldName: string;
    FieldType: string;
    FieldNN: integer;                   // 1 if notnull
    FieldDefault: string;
    FieldPK: integer;                   // 1 if primary key
  end;

  TRaSQLitePragma = class(TComponent)
  private
    FTempCacheSize: integer;
    FDefaultCacheSize: integer;
    FDefaultSynchronous: string;
    FDefaultTempStore: string;
    FTempStore: string;
    FSynchronous: string;
  protected
    function GetTempCacheSize: string;
    function GetDefaultCacheSize: string;
    function GetDefaultSynchronous: string;
    function GetDefaultTempStore: string;
    function GetTempStore: string;
    function GetSynchronous: string;
  published
    property TempCacheSize: integer read FTempCacheSize write FTempCacheSize;
    property DefaultCacheSize: integer read FDefaultCacheSize write FDefaultCacheSize;
    property DefaultSynchronous: string read FDefaultSynchronous
      write FDefaultSynchronous;
    property DefaultTempStore: string read FDefaultTempStore write FDefaultTempStore;
    property TempStore: string read FTempStore write FTempStore;
    property Synchronous: string read FSynchronous write FSynchronous;
  end;

  TRaSQLiteLog = class(TComponent)
  private
    FLogFile: string;
    FLogDebugOut: boolean;
    FAppend: boolean;
    FLogSQL: boolean;
    FLogInt: boolean;
  protected
  public
    procedure Display(Msg: string);
  published
    property LogFile: string read FLogFile write FLogFile;
    property LogDebugOut: boolean read FLogDebugOut write FLogDebugOut; // 20040225
    property Append: boolean read FAppend write FAppend;
    property LogSQL: boolean read FLogSQL write FLogSQL;
    property LogInternals: boolean read FLogInt write FLogInt;
  end;
  
  TRaSQLiteInlineSQL = class(TComponent)
  private
    FSQL: TStrings;
    procedure SetSQL(const Value: TStrings);
    function GetSQL: TStrings;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property SQL: TStrings read GetSQL write SetSQL;
  end;

  TRaSQLiteDB = class(TComponent)
  private
    FAfterConnect: TRaSQLiteNotifyEvent;
    FBeforeConnect: TRaSQLiteNotifyEvent;
    FAfterDisconnect: TRaSQLiteNotifyEvent;
    FBeforeDisconnect: TRaSQLiteNotifyEvent;
    FPassword: string;
    FUTF8DBName: Boolean;
    function FGetDefaultExt: string;
    function FGetDriverDLL: string;
  protected
    FInlineSQL: TRaSQLiteInlineSQL;
    FExecuteInlineSQL: boolean;
    FDatabase: string;
    FTransactionType: string;
    FSQLiteVersion: string;
    FDefaultExt: string;
    FDefaultDir: string;
    FDriverDll: string;
    FConnected: boolean;
    FMustExist: boolean;
    FVersion: string;
    FCharEnc: string;
    FUtf8: boolean;

    FSQLitePragma: TRaSQLitePragma;
    FSQLiteLog: TRaSQLiteLog;
    FLastError: string;
    SQLite3_Open: function(dbname: PAnsiChar; var db: pointer): integer; cdecl;
    SQLite3_Close: function(db: pointer): integer; cdecl;
    SQLite3_Exec: function(DB: Pointer; SQLStatement: PAnsiChar; Callback: TSQLite3_Callback;
                           UserDate: Pointer; var ErrMsg: PAnsiChar): Integer; cdecl;
    SQLite3_LibVersion: function(): PAnsiChar; cdecl;
    SQLite3_ErrorString: function(db: pointer): PAnsiChar; cdecl;
    SQLite3_GetTable: function(db: Pointer; SQLStatement: PAnsiChar; var ResultPtr: Pointer;
      var RowCount: cardinal; var ColCount: cardinal; var ErrMsg: PAnsiChar): integer; cdecl;
    SQLite3_FreeTable: procedure(Table: PAnsiChar); cdecl;
    SQLite3_FreeMem: procedure(P: PAnsiChar); cdecl;
    SQLite3_Complete: function(P: PAnsiChar): boolean; cdecl;
    SQLite3_LastInsertRow: function(db: Pointer): integer; cdecl;
    SQLite3_Cancel: procedure(db: Pointer); cdecl;
    SQLite3_BusyHandler: procedure(db: Pointer; CallbackPtr: Pointer; Sender: TObject); cdecl;
    SQLite3_BusyTimeout: procedure(db: Pointer; TimeOut: integer); cdecl;
    SQLite3_Changes: function(db: Pointer): integer; cdecl;
    SQLite3_Prepare: function(db: Pointer; SQLStatement: PAnsiChar; nBytes: integer;
      var hstatement: pointer; var Tail: PAnsiChar): integer; cdecl;
    SQLite3_Finalize: function(hstatement: pointer): integer; cdecl;
    SQLite3_Reset: function(hstatement: pointer): integer; cdecl;
    SQLite3_Step: function(hstatement: pointer): integer; cdecl;
    SQLite3_Column_blob: function(hstatement: pointer; iCol: integer): pointer; cdecl;
    SQLite3_Column_bytes: function(hstatement: pointer; iCol: integer): integer; cdecl;
    SQLite3_Column_count: function(hstatement: pointer): integer; cdecl;
    SQLite3_Column_decltype: function(hstatement: pointer; iCol: integer): PAnsiChar; cdecl;
    SQLite3_Column_double: function(hstatement: pointer; iCol: integer): double; cdecl;
    SQLite3_Column_int: function(hstatement: pointer; iCol: integer): integer; cdecl;
    SQLite3_Column_int64: function(hstatement: pointer; iCol: integer): int64; cdecl;
    SQLite3_Column_name: function(hstatement: pointer; iCol: integer): PAnsiChar; cdecl;
    SQLite3_Column_text: function(hstatement: pointer; iCol: integer): PAnsiChar; cdecl;
    SQLite3_Column_text16: function(hstatement: pointer; iCol: integer): PWideChar; cdecl;
    SQLite3_Column_type: function(hstatement: pointer; iCol: integer): integer; cdecl;
    SQLite3_Bind_Blob: function(hstatement: pointer; iCol: integer; buf: PAnsiChar; n: integer; DestroyPtr: Pointer): integer; cdecl;
    SQLite3_Bind_Text16: function(hstatement: pointer; iCol: integer; buf: pointer; n: integer; DestroyPtr: Pointer): integer; cdecl;
    SQLite3_Bind_Parameter_Count: function(hstatement: pointer): integer; cdecl;
    SQLite3_Key: function(db: Pointer; Password: PChar; Length: Integer): Integer; cdecl;
    SQLite3_Rekey: function (db: Pointer; Password: PChar; Length: Integer): Integer; cdecl;

    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure DBConnect(Connected: boolean);
    function SQLite3_PrepareResult(DB: Pointer; TheStatement: string; FParams: TParams; Sender: TObject) : pointer;
    function SQLite3_GetNextResult(DB: Pointer; TheStatement: pointer; FParams: TParams; Sender: TObject) : pointer;
    procedure SQLite3_CloseResult(TheStatement : pointer);
  public
    DLLHandle: THandle;
    DBHandle: Pointer;
    // SQLite3_Key: function(db: Pointer; Password: PChar; Length: Integer): Integer; cdecl;
    // SQLite3_Rekey: function (db: Pointer; Password: PChar; Length: Integer): Integer; cdecl;

    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function LoadLibs: boolean;
    procedure FSetDatabase(Database: string);
    function RowsAffected: integer;
    function  TableExists(const ATableName: AnsiString): Boolean;
    procedure ExecStartTransaction(TransType: string);
    procedure StartTransaction;
    procedure StartDeferredTransaction;
    procedure StartImmediateTransaction;
    procedure StartExclusiveTransaction;
    procedure Open;
    procedure Close;
    procedure Commit;
    procedure RollBack;
    procedure ShowDatabases(List: TStrings);
    procedure GetTableNames(List: TStrings; SystemTables: boolean = false);
    procedure GetTableInfo(TableName: string; List: TList);
    procedure GetIndexNames(List: TStrings; SystemTables: boolean = false);
    procedure GetFieldNames(TableName: string; List: TStrings);
    procedure GetPrimaryKeys(TableName: string; List: TStrings);
    procedure GetTableIndexNames(TableName: string; List: TStrings);
    procedure ExecPragma;
    function SQLite_XExec(db: Pointer; SQLStatement: PAnsiChar; CallbackPtr: Pointer; Sender: TObject; var ErrMsg: PAnsiChar): integer; cdecl;
    function SQLite3_Execute(db: Pointer; TheStatement: string; FParams: TParams; Sender: TObject): integer;
    function SQLite3_ExecSQL(TheStatement: string; Blobs: TList=nil): integer;
    procedure ShowError;
    function GetUserVersion(database : string=''): integer;
    procedure SetUserVersion(Version : integer; Database : string='');
    function CheckPassword: Boolean;
    function ChangePassword(AOldPwd, ANewPwd: string): boolean;
  published
    property CharacterEncoding: string read FCharEnc write FCharEnc;
    property TransactionType: string read FTransactionType write FTransactionType;
    property Database: string read FDatabase write FSetDatabase;
    property SQLitePragma: TRaSQLitePragma read FSQLitePragma write FSQLitePragma;
    property DefaultExt: string read FGetDefaultExt write FDefaultExt;
    property DefaultDir: string read FDefaultDir write FDefaultDir;
    property Version: string read FVersion write FVersion;
    property DriverDLL: string read FGetDriverDLL write FDriverDLL;
    property Connected: boolean read FConnected write DBConnect;
    property MustExist: boolean read FMustExist write FMustExist;
    property SQLiteInlineSQL: TRaSQLiteInlineSQL read FInlineSQL write FInlineSQL;
    property ExecuteInlineSQL: boolean read FExecuteInlineSQL write FExecuteInlineSQL;
    property AfterConnect: TRaSQLiteNotifyEvent read FAfterConnect write FAfterConnect;
    property BeforeConnect: TRaSQLiteNotifyEvent read FBeforeConnect write FBeforeConnect;
    property AfterDisconnect: TRaSQLiteNotifyEvent
      read FAfterDisconnect write FAfterDisconnect;
    property BeforeDisconnect: TRaSQLiteNotifyEvent
      read FBeforeDisconnect write FBeforeDisconnect;
    property Password: string read FPassword write FPassword;
    property UTF8DBName: Boolean read FUTF8DBName write FUTF8DBName;
  end;

  RaSQLiteError = class(Exception);

  PRecInfo = ^TRecInfo;

  TRecInfo = packed record
    Bookmark : integer;
    BookmarkFlag : TBookmarkFlag;
  end;

  TRaSQLiteBaseQuery = class;

  TFResult = class
  protected
    Data: TList;
    BookMark: TList;
    RowId: TList;
    FLastBookmark: integer;
    FBufSize: integer;
    FDataSet: TRaSQLiteBaseQuery;
  public
    constructor Create(TheDataSet: TRaSQLiteBaseQuery);
    destructor Destroy; override;
    procedure FreeBlobs;
    procedure SetBufSize(TheSize: integer);
    procedure Add(TheBuffer: PAnsiChar; TheRowId: integer);
    procedure Insert(Index: integer; TheBuffer: Pointer; TheRowId: integer);
    procedure Delete(Index: integer);
    function GetData(Index: integer): Pointer;
    function Count: integer;
    function IndexOf(TheBookMark: pointer): integer;
    function GetBookmark(Index: integer): integer;
    function GetRowId(Index: integer): integer;
  end;

  TRaSQLiteUpdateSQL = class(TComponent)
  private
    FInsertSQL: TStrings;
    FUpdateSQL: TStrings;
    FDeleteSQL: TStrings;
    procedure SetInsertSQL(const Value: TStrings);
    procedure SetUpdateSQL(const Value: TStrings);
    procedure SetDeleteSQL(const Value: TStrings);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property InsertSQL: TStrings read FInsertSQL write SetInsertSQL;
    property UpdateSQL: TStrings read FUpdateSQL write SetUpdateSQL;
    property DeleteSQL: TStrings read FDeleteSQL write SetDeleteSQL;
  end;

  TRaSQLiteOutput = class(TComponent)
  private
    FActive: boolean;
    FOutputType: string;
    FTableClass: string;
    FHeaderClass: string;
    FCellClass: string;
    FOutput: TStrings;
    FSeparator: string;
    FDataSource: TDataSource;
    procedure SetOutput(const Value: TStrings);
    procedure SetFActive(Active: boolean);
    function GetOutput: TStrings;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Execute(MyDataSet: TDataSet);
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  published
    property Active: boolean read FActive write SetFActive;
    property DataSource: TDataSource read FDataSource write FDataSource;
    property OutputType: string read FOutputType write FOutputType;
    property TableClass: string read FTableClass write FTableClass;
    property HeaderClass: string read FHeaderClass write FHeaderClass;
    property CellClass: string read FCellClass write FCellClass;
    property Output: TStrings read GetOutput write SetOutput;
    property FieldSeparator: string read FSeparator write FSeparator;
  end;

  TRaSQLiteBaseQuery = class(TDataSet)
  private
    FParams: TParams;
    FTypeLess: boolean;
    FNoResults: boolean;
    FAutoCommit: boolean;
    FTransactionType: string;
    FTableDateFormat: string;
    FSQLiteDateFormat: boolean;
    FResult: TFResult;
    FSQL: TStrings;
    FSQLCursor: boolean;
    FPrepared: string;
    FRecBufSize: integer;
    FRecInfoOfs: integer;
    FCurRec: integer;
    FMasterFields: string;
    FMasterSource: TDataSource;
    FSaveChanges: boolean;
    MaxStrLen: integer;
    FConnection: TRaSQLiteDB;
    FReadOnly: boolean;
    FMaxResults: integer;
    FStartResult: integer;
    FUniDir : boolean;
    FStatement : pointer;
    CurrentRowId: integer;
    SQLStr: string;
    ResultStr: PAnsiChar;
    RowId : integer;
    RowIdCol : integer;
    DetailList: TList;
    procedure SetSQL(const Value: TStrings);
    function UnpackBuffer(Buffer: PAnsiChar; FieldType: TFieldType): TConvertBuffer;
    procedure SetDataSource(Value: TDataSource);
  protected
    
    function SetQueryParams(InStr: string): string;
    procedure SetParamsList(Value: TParams);
    function GetParamsCount: word;
    procedure RegisterDetailDataset(DetailDataSet: TRaSQLiteBaseQuery);
    procedure LoadQueryData;
    function GetActiveBuffer(var Buffer: PAnsiChar): boolean;
    function GetDataSource: TDataSource; override;
    procedure NotifySQLiteMasterChanged;
    function AllocRecordBuffer: PAnsiChar; override;
    procedure FreeRecordBuffer(var Buffer: PAnsiChar); override;
    procedure GetBookmarkData(Buffer: PAnsiChar; Data: Pointer); override;
    function GetBookmarkFlag(Buffer: PAnsiChar): TBookmarkFlag; override;
    function GetRecord(Buffer: PAnsiChar; GetMode: TGetMode;
      DoCheck: boolean): TGetResult; override;
    function GetRecordSize: word; override;
    procedure InternalAddRecord(Buffer: Pointer; Append: boolean); override;
    procedure InternalClose; override;
    procedure InternalDelete; override;
    procedure InternalFirst; override;
    procedure InternalGotoBookmark(Bookmark: Pointer); override;
    procedure InternalHandleException; override;
    procedure InternalInitFieldDefs; override;
    procedure InternalInitRecord(Buffer: PAnsiChar); override;
    procedure InternalLast; override;
    procedure InternalOpen; override;
    procedure InternalPost; override;
    procedure InternalSetToRecord(Buffer: PAnsiChar); override;
    procedure OpenCursor(InfoQuery: Boolean); override; // GPA
    function IsCursorOpen: boolean; override;
    procedure SetBookmarkFlag(Buffer: PAnsiChar; Value: TBookmarkFlag); override;
    procedure SetBookmarkData(Buffer: PAnsiChar; Data: Pointer); override;
    procedure SetFieldData(Field: TField; Buffer: Pointer); override;
    function GetFieldSize(FieldNo: integer): integer; overload;
    function GetFieldSize(Field: TField): integer; overload;
    function GetNativeFieldSize(FieldNo: integer): integer;
    function GetFieldOffset(FieldNo: integer): integer;
    function GetCalcFieldOffset(Field: TField): integer;
    function GetMasterFields: string;
    procedure SetMasterFields(const Value: string);
    function GetRecordCount: integer; override;
    function GetRecNo: integer; override;
    procedure SetRecNo(Value: integer); override;
    property BaseSQL: TStrings read FSQL write SetSQL;
    procedure SetSQLiteDateFormat(const Value: boolean);
    procedure SetFilterText(const Value: string); override;
    procedure DataConvert(Field: TField; Source, Dest: Pointer; ToNative: Boolean); override;
    function CalcFieldInList(const List: string): Boolean; 
    // procedure PSGetAttributes(List: TList); virtual;
    // function PSGetDefaultOrder: TIndexDef; virtual;
    // function PSGetIndexDefs(IndexTypes: TIndexOptions): TIndexDefs; virtual;
    procedure PSEndTransaction(Commit: Boolean); override;
    procedure PSExecute; override;
    function PSExecuteStatement(const ASQL: string; AParams: TParams; ResultSet: Pointer = nil): Integer; override;
    function PSGetParams: TParams; override;
    function PSGetTableName: string; override;
    function PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError; override;
    function PSInTransaction: Boolean; override;
    function PSIsSQLBased: Boolean; override;
    function PSIsSQLSupported: Boolean; override;
    procedure PSReset; override;
    procedure PSSetCommandText(const CommandText: string); override;
    procedure PSSetParams(AParams: TParams); override;
    procedure PSStartTransaction; override;
    function PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean; override;
    function PSGetQuoteChar: string; override;
    function PSGetKeyFields: string; override;

  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure ExecSQL;
    procedure StartTransaction;
    procedure StartDeferredTransaction;
    procedure StartImmediateTransaction;
    procedure StartExclusiveTransaction;
    procedure Commit;
    procedure RollBack;
    procedure SetFiltered(Value: Boolean); override;
    procedure SQLiteMasterChanged; virtual;
    function GetFieldData(Field: TField; Buffer: Pointer): boolean; override;
    function GetFieldData(FieldNo: integer; Buffer: Pointer): boolean; override;
    function GetLastInsertRow: integer;
    function GetFieldData(Field: TField; Buffer: Pointer; NativeFormat: boolean): boolean; override;
    function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
    function Locate(const KeyFields: string; const KeyValues: variant; Options: TLocateOptions): boolean; override;
    function BookmarkValid(Bookmark: Pointer): boolean; override;
    function LocateNearest(const KeyFields: String; const KeyValues: Variant; Options: TLocateOptions): Boolean;
    property Params: TParams read FParams write SetParamsList stored false;
    function Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant; override;
  published
    property AutoCommit: boolean read FAutoCommit write FAutoCommit default true;
    property TransactionType: string read FTransactionType write FTransactionType;
    property SQLiteDateFormat: boolean read FSQLiteDateFormat write SetSQLiteDateFormat;
    property TableDateFormat: string read FTableDateFormat write FTableDateFormat;
    property Connection: TRaSQLiteDB read FConnection write FConnection;
    property MaxResults: integer read FMaxResults write FMaxResults;
    property StartResult: integer read FStartResult write FStartResult;
    property TypeLess: boolean read FTypeLess write FTypeLess;
    property MasterFields: string read GetMasterFields write SetMasterFields;
    property MasterSource: TDataSource read GetDataSource write SetDataSource;
    property SQLCursor: boolean read FSQLCursor write FSQLCursor;
    property ReadOnly: boolean read FreadOnly write FReadOnly;
    property UniDirectional : boolean read FUniDir write FUniDir;
    property AutoCalcFields;
    property Filter;
    property Filtered;
    property Active;
    property BeforeOpen;
    property AfterOpen;
    property BeforeClose;
    property AfterClose;
    property BeforeInsert;
    property AfterInsert;
    property BeforeEdit;
    property AfterEdit;
    property BeforePost;
    property AfterPost;
    property BeforeCancel;
    property AfterCancel;
    property BeforeDelete;
    property AfterDelete;
    property BeforeScroll;
    property AfterScroll;
    property BeforeRefresh;
    property AfterRefresh;
    property OnCalcFields;
    property OnDeleteError;
    property OnEditError;
    property OnNewRecord;
    property OnPostError;
  end;

  TRaSQLiteQuery = class(TRaSQLiteBaseQuery)
  private
    FUpdateSQL: TRaSQLiteUpdateSQL;
    FRawSQL: boolean;
    procedure SetSQL(const Value: TStrings);
    function GetSQL: TStrings;
    procedure QueryChanged(Sender: TObject);
  protected
    procedure InternalOpen; override;
    procedure InternalPost; override;
    procedure InternalDelete; override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure InternalClose; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    
    procedure SQLiteMasterChanged; override;
  published
    property RawSQL: boolean read FRawSQL write FRawSQL;
    property SQL: TStrings read GetSQL write SetSQL;
    property UpdateSQL: TRaSQLiteUpdateSQL read FUpdateSQL write FUpdateSQL;
  end;

  TRaSQLiteTable = class(TRaSQLiteBaseQuery)
  private
    FTableName: string;
    FPrimaryAutoInc: boolean;
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure InternalOpen; override;
    procedure InternalPost; override;
    procedure InternalDelete; override;
    procedure SetFTableName(TableName : string);
  public
    procedure SQLiteMasterChanged; override;
  published
    property TableName: string read FTableName write SetFTableName;
    property PrimaryAutoInc: boolean read FPrimaryAutoInc write FPrimaryAutoInc;
  end;

  TRaSQLiteBlobStream = class(TMemoryStream)
  private
    FField: TBlobField;
    FDataSet: TRaSQLiteBaseQuery;
    FMode: TBlobStreamMode;
    FModified: Boolean;
    FOpened: Boolean;
    procedure LoadBlobData;
    procedure SaveBlobData;
  public
    constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
    destructor Destroy; override;
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
  end;

implementation

var
  DebugSpaces       : Integer = 0;

procedure Debug(const S: string);
begin
  OutputDebugString(PAnsiChar(StringOfChar(' ', DebugSpaces) + S));
end;

function SyntaxCheck(LWord, RWord: string): boolean;
begin
  try
    if CompareText(LWord, RWord) <> 0 then begin
      SyntaxCheck := false;
      raise RaSQLiteError.Create('SQL macro syntax error on sql, expected ' + RWord)
    end else
      SyntaxCheck := true;
  finally

  end;
end;

function SQLiteErrorStr(SQLiteErrorCode: Integer): string;
begin
  case SQLiteErrorCode of
    SQLITE_OK: Result := 'Successful result';
    SQLITE_ERROR: Result := 'SQL error or missing database';
    SQLITE_INTERNAL: Result := 'An internal logic error in SQLite';
    SQLITE_PERM: Result := 'Access permission denied';
    SQLITE_ABORT: Result := 'Callback routine requested an abort';
    SQLITE_BUSY: Result := 'The database file is locked';
    SQLITE_LOCKED: Result := 'A table in the database is locked';
    SQLITE_NOMEM: Result := 'A malloc() failed';
    SQLITE_READONLY: Result := 'Attempt to write a readonly database';
    SQLITE_INTERRUPT: Result := 'Operation terminated by sqlite3_interrupt()';
    SQLITE_IOERR: Result := 'Some kind of disk I/O error occurred';
    SQLITE_CORRUPT: Result := 'The database disk image is malformed';
    SQLITE_NOTFOUND: Result := '(Internal Only) Table or record not found';
    SQLITE_FULL: Result := 'Insertion failed because database is full';
    SQLITE_CANTOPEN: Result := 'Unable to open the database file';
    SQLITE_PROTOCOL: Result := 'Database lock protocol error';
    SQLITE_EMPTY: Result := 'Database is empty';
    SQLITE_SCHEMA: Result := 'The database schema changed';
    SQLITE_TOOBIG: Result := 'Too much data for one row of a table';
    SQLITE_CONSTRAINT: Result := 'Abort due to contraint violation';
    SQLITE_MISMATCH: Result := 'Data type mismatch';
    SQLITE_MISUSE: Result := 'Library used incorrectly';
    SQLITE_NOLFS: Result := 'Uses OS features not supported on host';
    SQLITE_AUTH: Result := 'Authorization denied';
    SQLITE_FORMAT: Result := 'Auxiliary database format error';
    SQLITE_RANGE: Result := '2nd parameter to sqlite3_bind out of range';
    SQLITE_NOTADB: Result := 'File opened that is not a database file';
    SQLITE_ROW: Result := 'sqlite3_step() has another row ready';
    SQLITE_DONE: Result := 'sqlite3_step() has finished executing';
  else
    Result := 'Unknown SQLite Error Code "' + IntToStr(SQLiteErrorCode) + '"';
  end;
end;

procedure GetFieldInfo(FieldInfo: string; var FieldType: TFieldType;
  var FieldLen, FieldDec: integer);
var
  p1, p2, pn        : integer;
  vt                : string;
begin
  FieldType := ftString;
  FieldLen := 255;
  FieldDec := 0;

  p1 := pos('(', FieldInfo);
  if p1 <> 0 then
  begin
    p2 := pos(')', FieldInfo);
    if p2 <> 0 then
    begin
      vt := LowerCase(Copy(FieldInfo, 1, p1 - 1));
      if (vt = 'varchar') or (vt = 'char') or (vt = 'varchar2') then begin
        FieldType := ftString;
        FieldLen := StrToInt(Copy(FieldInfo, p1 + 1, p2 - p1 - 1));
      end else if (vt = 'nvarchar') or (vt = 'nchar') or (vt = 'nvarchar2') then begin
        FieldType := ftWideString;
        FieldLen := StrToInt(Copy(FieldInfo, p1 + 1, p2 - p1 - 1)) * 2;
      end else if (vt = 'numeric') then begin
        vt := Copy(FieldInfo, p1 + 1, p2 - p1 - 1);
        pn := pos('.', vt); if pn = 0 then pn := pos(',', vt);
        FieldType := ftFloat;
        if pn = 0 then begin
          FieldLen := StrToInt(vt);
          FieldDec := 0;
        end else begin
          FieldLen := StrToInt(Copy(vt, 1, pn - 1));
          FieldDec := StrToInt(Copy(vt, pn + 1, 2));
        end;
      end;
    end
    else
      FieldLen := 256;
  end
  else
  begin
    vt := LowerCase(FieldInfo);
    if vt = 'date' then
    begin
      FieldType := ftDate;
      FieldLen := 10;
    end
    else if vt = 'datetime' then
    begin
      FieldType := ftDateTime;
      FieldLen := 24;
    end
    else if vt = 'time' then
    begin
      FieldType := ftTime;
      FieldLen := 12;
    end
    else if vt = 'timestamp' then
    begin
      FieldType := ftTimeStamp;
      FieldLen := 12;
    end
    else if (vt = 'integer') or (vt = 'int')  then
    begin
      FieldType := ftInteger;
      FieldLen := 12;
    end
    else if (vt = 'float') or (vt = 'real') then
    begin
      FieldType := ftFloat;
      FieldLen := 12;
    end
    else if (vt = 'boolean') or (vt = 'logical') then
    begin
      FieldType := ftBoolean;
      FieldLen := 2;
    end
    else if (vt='text') or (vt = 'shorttext') or (vt = 'string') then
    begin
      FieldType := ftString;
      FieldLen := 255;
    end
    else if (vt = 'widetext') or (vt = 'widestring') then
    begin
      FieldType := ftWideString;
      FieldLen := 512;
    end
    else if (vt = 'currency') or (vt = 'financial') or (vt = 'money') then
    begin
      FieldType := ftCurrency;
      FieldLen := 10;
    end
    else if (vt = 'blob') then
    begin
      FieldType := ftBlob;
      FieldLen := SizeOf(Pointer);
    end
    else if (vt = 'graphic') then
    begin
      FieldType := ftGraphic;
      FieldLen := SizeOf(Pointer);
    end
    else if (vt = 'clob') or (vt = 'memo') or (vt = 'text') or (vt = 'longtext') then
    begin
      FieldType := ftMemo;
      FieldLen := SizeOf(Pointer);
    end; 
  end;
end;

function DateTimeToNative(DataType: TFieldType; Data: TDateTime): TDateTimeRec;
var
  TimeStamp         : TTimeStamp;
begin
  TimeStamp := DateTimeToTimeStamp(Data);
  case DataType of
    ftDate: Result.Date := TimeStamp.Date;
    ftTime: Result.Time := TimeStamp.Time;
  else
    Result.DateTime := TimeStampToMSecs(TimeStamp);
  end;
end;

procedure ApplicationHandleException(Sender: TObject);
begin
  if Assigned(Classes.ApplicationHandleException) then
    Classes.ApplicationHandleException(Sender);
end;

procedure TRaSQLiteLog.Display(Msg: string);
var
  fn                : Textfile;
begin
  if FileExists(FLogFile) then
  begin
    if FAppend then
    begin
      AssignFile(fn, FLogFile);
      System.Append(fn);
    end
    else
    begin
      SysUtils.DeleteFile(FLogFile);
      AssignFile(fn, FLogFile);
      Rewrite(fn);
    end;
  end
  else
  begin
    AssignFile(fn, FLogFile);
    Rewrite(fn);
  end;
  Writeln(fn, FormatDateTime('yyyy mmm dd (hh:nn:ss) ', now) + Msg);
  CloseFile(fn);
end;

function TRaSQLitePragma.GetTempCacheSize: string;
begin
  GetTempCacheSize := 'pragma cache_size=' + IntToStr(FTempCacheSize);
end;

function TRaSQLitePragma.GetDefaultCacheSize: string;
begin
  GetDefaultCacheSize := 'pragma default_cache_size=' + IntToStr(FDefaultCacheSize);
end;

function TRaSQLitePragma.GetDefaultSynchronous: string;
begin
  GetDefaultSynchronous := 'pragma default_synchronous=' + FDefaultSynchronous;
end;

function TRaSQLitePragma.GetDefaultTempStore: string;
begin
  GetDefaultTempStore := 'pragma default_temp_store=' + FDefaultTempStore;

end;

function TRaSQLitePragma.GetTempStore: string;
begin
  GetTempStore := 'pragma temp_store=' + FTempStore;
end;

function TRaSQLitePragma.GetSynchronous: string;
begin
  GetSynchronous := 'pragma synchronous=' + FSynchronous;
end;

constructor TFResult.Create(TheDataSet: TRaSQLiteBaseQuery);
begin
  Data := TList.Create;
  Bookmark := TList.Create;
  RowId := TList.Create;
  FDataSet := TheDataset;
  FLastBookmark := -1;
end;

destructor TFResult.Destroy;
var
  ptr               : Pointer;
  i                 : integer;
begin
  FreeBlobs;
  if Assigned(Data) then begin
    for i := 0 to Data.Count - 1 do begin
      ptr := Data.Items[i];
      if Assigned(ptr) then FreeMem(ptr, FBufSize);
    end;
    Data.Free;
    Data := nil;
  end;

  if Assigned(Bookmark) then begin
    Bookmark.Free;
    Bookmark := nil;
  end;

  if Assigned(RowId) then begin
    RowId.Free;
    RowId := nil;
  end;

end;

procedure TFResult.FreeBlobs;
var i, j            : integer;
  offset            : integer;
  ptr               : PAnsiChar;
  stream            : TMemoryStream;
begin
  if not Assigned(FDataSet) then exit;
  if not Assigned(FDataSet.FieldList) then exit;
  for j := 0 to Data.Count - 1 do begin
    ptr := GetData(j);
    for i := 0 to FDataSet.FieldList.Count - 1 do begin
      if FDataSet.FieldList[i].DataType in [ftMemo, ftFmtMemo, ftGraphic, ftBlob] then begin
        Offset := FDataset.GetFieldOffset(FDataSet.FieldList[i].FieldNo);
        Move((ptr + Offset)^, Pointer(Stream), sizeof(Pointer));
        Stream.Free;
      end;
    end;
  end;
end;

procedure TFResult.SetBufSize(TheSize: integer);
begin
  FBufSize := TheSize;
end;

procedure TFResult.Add(TheBuffer: PAnsiChar; TheRowId: integer);
var
  ptr               : PAnsiChar;
begin
  Inc(FLastBookmark);
  GetMem(Ptr, FBufSize);
  move(TheBuffer^, ptr^, FBufSize);
  Data.Add(Ptr);
  Bookmark.Add(Pointer(FLastBookMark));
  if TheRowId >= 0 then
    RowId.Add(Pointer(TheRowId))
  else
    RowId.Add(Pointer(RowId.Count));
end;

procedure TFResult.Insert(Index: integer; TheBuffer: pointer; TheRowId: integer);
var
  ptr               : Pointer;
begin
  Inc(FLastBookmark);
  GetMem(Ptr, FBufSize);
  move(TheBuffer^, ptr^, FBufSize);
  if Data.Count < Index then begin
    Data.Add(Ptr);
    Bookmark.Add(Pointer(FLastBookMark));
    RowId.Add(Pointer(TheRowId));
  end else begin
    Data.Insert(Index, Ptr);
    Bookmark.Insert(Index, Pointer(FLastBookMark));
    RowId.Insert(Index, Pointer(TheRowId));
  end;
end;

procedure TFResult.Delete(Index: integer);
var
  ptr               : pointer;
begin
  if not ((Index < 0) or (Index >= Data.Count)) then
  begin
    ptr := Data.Items[Index];
    if ptr <> nil then
      FreeMem(ptr, FBufSize);
    Data.Delete(Index);
    Bookmark.Delete(Index);
    Rowid.Delete(Index);
  end;
end;

function TFResult.GetData(Index: integer): Pointer;
begin
  if (Index < 0) or (Index >= Data.Count) then
    GetData := nil
  else
    GetData := Data.Items[Index];
end;

function TFResult.GetBookmark(Index: integer): integer;
begin
  if (Index < 0) or (Index >= Data.Count) then
    GetBookmark := -1
  else
    GetBookmark := integer(Bookmark.Items[Index]);
end;

function TFResult.GetRowId(Index: integer): integer;
begin
  if (Index < 0) or (Index >= RowId.Count) then
    GetRowId := -1
  else
    GetRowId := integer(RowId.Items[Index]);
end;

function TFResult.Count: integer;
begin
  Count := Data.Count;
end;

function TFResult.IndexOf(TheBookMark: pointer): integer;
begin
  Result := BookMark.IndexOf(TheBookmark);
end;

procedure TRaSQLiteDB.Notification(AComponent: TComponent; Operation: TOperation);
begin
  if Assigned(AComponent) then
  begin
    if (Operation = opRemove) then
    begin
      if (AComponent is TRaSQLitePragma) then begin
        if Assigned(FSQLitePragma) then begin
          if TRaSQLitePragma(AComponent) = FSQLitePragma then
            FSQLitePragma := nil;
        end;
      end
      else if (AComponent is TRaSQLiteLog) then
      begin
        if Assigned(FSQLiteLog) then begin
          if TRaSQLiteLog(AComponent) = FSQLiteLog then
            FSQLiteLog := nil;
        end;
      end
      else if (AComponent is TRaSQLiteInlineSQL) then
      begin
        if Assigned(FInlineSQL) then begin
          if TRaSQLiteInlineSQL(AComponent) = FInlineSQL then
            FInlineSQL := nil;
        end;
      end;
    end;
  end;
  inherited;
end;

function TRaSQLiteDB.LoadLibs: boolean;
begin
  try
    if not(DecimalSeparator in ['.',',']) then
       DecimalSeparator := '.';
    OutputDebugString('loading sqlite lib');
    OutputDebugString(PAnsiChar(DriverDLL));
    Result := false;
    DLLHandle := LoadLibrary(PAnsiChar(DriverDLL));  //JohnLito
    if DLLHandle <> 0 then
    begin
      @SQLite3_Open := GetProcAddress(DLLHandle, 'sqlite3_open');
      if not Assigned(@SQLite3_Open) then exit;
      @SQLite3_Close := GetProcAddress(DLLHandle, 'sqlite3_close');
      if not Assigned(@SQLite3_Close) then exit;
      @SQLite3_Exec := GetProcAddress(DLLHandle, 'sqlite3_exec');
      if not Assigned(@SQLite3_Exec) then exit;
      @SQLite3_LibVersion := GetProcAddress(DLLHandle, 'sqlite3_libversion');
      if not Assigned(@SQLite3_LibVersion) then exit;
      @SQLite3_ErrorString := GetProcAddress(DLLHandle, 'sqlite3_errmsg');
      if not Assigned(@SQLite3_ErrorString) then exit;
      @SQLite3_GetTable := GetProcAddress(DLLHandle, 'sqlite3_get_table');
      if not Assigned(@SQLite3_GetTable) then exit;
      @SQLite3_FreeTable := GetProcAddress(DLLHandle, 'sqlite3_free_table');
      if not Assigned(@SQLite3_FreeTable) then exit;
      @SQLite3_FreeMem := GetProcAddress(DLLHandle, 'sqlite3_free');
      if not Assigned(@SQLite3_FreeMem) then exit;
      @SQLite3_Complete := GetProcAddress(DLLHandle, 'sqlite3_complete');
      if not Assigned(@SQLite3_Complete) then exit;
      @SQLite3_LastInsertRow := GetProcAddress(DLLHandle, 'sqlite3_last_insert_rowid');
      if not Assigned(@SQLite3_LastInsertRow) then exit;
      @SQLite3_Cancel := GetProcAddress(DLLHandle, 'sqlite3_interrupt');
      if not Assigned(@SQLite3_Cancel) then exit;
      @SQLite3_BusyTimeout := GetProcAddress(DLLHandle, 'sqlite3_busy_timeout');
      if not Assigned(@SQLite3_BusyTimeout) then exit;
      @SQLite3_BusyHandler := GetProcAddress(DLLHandle, 'sqlite3_busy_handler');
      if not Assigned(@SQLite3_BusyHandler) then exit;
      @SQLite3_Changes := GetProcAddress(DLLHandle, 'sqlite3_changes');
      if not Assigned(@SQLite3_Changes) then exit;
      @SQLite3_Prepare := GetProcAddress(DLLHandle, 'sqlite3_prepare');
      if not Assigned(@SQLite3_Prepare) then exit;
      @SQLite3_Finalize := GetProcAddress(DLLHandle, 'sqlite3_finalize');
      if not Assigned(@SQLite3_Finalize) then exit;
      @SQLite3_Reset := GetProcAddress(DLLHandle, 'sqlite3_reset');
      if not Assigned(@SQLite3_Reset) then exit;
      @SQLite3_Step := GetProcAddress(DLLHandle, 'sqlite3_step');
      if not Assigned(@SQLite3_Step) then exit;
      @SQLite3_Column_blob := GetProcAddress(DLLHandle, 'sqlite3_column_blob');
      if not Assigned(@SQLite3_Column_blob) then exit;
      @SQLite3_Column_bytes := GetProcAddress(DLLHandle, 'sqlite3_column_bytes');
      if not Assigned(@SQLite3_Column_bytes) then exit;
      @SQLite3_Column_count := GetProcAddress(DLLHandle, 'sqlite3_column_count');
      if not Assigned(@SQLite3_Column_Count) then exit;
      @SQLite3_Column_decltype := GetProcAddress(DLLHandle, 'sqlite3_column_decltype');
      if not Assigned(@SQLite3_Column_decltype) then exit;
      @SQLite3_Column_double := GetProcAddress(DLLHandle, 'sqlite3_column_double');
      if not Assigned(@SQLite3_Column_double) then exit;
      @SQLite3_Column_int := GetProcAddress(DLLHandle, 'sqlite3_column_int');
      if not Assigned(@SQLite3_Column_int) then exit;
      @SQLite3_Column_int64 := GetProcAddress(DLLHandle, 'sqlite3_column_int64');
      if not Assigned(@SQLite3_Column_int64) then exit;
      @SQLite3_Column_name := GetProcAddress(DLLHandle, 'sqlite3_column_name');
      if not Assigned(@SQLite3_Column_name) then exit;
      @SQLite3_Column_text := GetProcAddress(DLLHandle, 'sqlite3_column_text');
      if not Assigned(@SQLite3_Column_text) then exit;
      @SQLite3_Column_text16 := GetProcAddress(DLLHandle, 'sqlite3_column_text16');
      if not Assigned(@SQLite3_Column_text16) then exit;
      @SQLite3_Column_type := GetProcAddress(DLLHandle, 'sqlite3_column_type');
      if not Assigned(@SQLite3_Column_type) then exit;
      @SQLite3_Bind_Blob := GetProcAddress(DLLHandle, 'sqlite3_bind_blob');
      if not Assigned(@SQLite3_Bind_blob) then exit;
      @SQLite3_Bind_Text16 := GetProcAddress(DLLHandle, 'sqlite3_bind_text16');
      if not Assigned(@SQLite3_Bind_Text16) then exit;
      @SQLite3_Bind_Parameter_Count := GetProcAddress(DLLHandle, 'sqlite3_bind_parameter_count');
      if not Assigned(@SQLite3_Bind_Parameter_Count) then exit;
      @SQLite3_Key := GetProcAddress(DLLHandle, 'sqlite3_key');
      if not Assigned(@SQLite3_Key) then exit;
      @SQLite3_Rekey := GetProcAddress(DLLHandle, 'sqlite3_rekey');
      if not Assigned(@SQLite3_Rekey) then exit;
      Result := true; 
    end;
  finally
  end;
end;

procedure TRaSQLiteDB.ShowError;
var msg             : PAnsiChar;
begin
  msg := SQLite3_ErrorString(DBHandle);
  raise EDatabaseError.Create(msg);
end;

function TRaSQLiteDB.SQLite3_ExecSQL(TheStatement: string; Blobs: TList=nil): integer; 
var
 PF: PAnsiChar;
 p: Pointer;
 i: Integer;
 b: Integer;
 m: TMemoryStream;
begin
 TheStatement := StringReplace(TheStatement, #2, '?', [rfReplaceAll, rfIgnoreCase]);
 PF := PAnsiChar(TheStatement);
 repeat
   Result := SQLite3_Prepare(DBHandle, PF, -1, p, PF);
   if Result = SQLITE_OK then
     begin
       if Assigned(Blobs) then begin
         for i := 0 to Blobs.Count - 1 do Begin
           b := i+1;
           m := TMemoryStream(Blobs.Items[i]);
           SQLite3_Bind_Blob(p, b, PChar(m.Memory), m.Size, nil);
         End;
       end;
       repeat
       until SQLite3_Step(p) in [SQLITE_DONE, SQLITE_ERROR, SQLITE_MISUSE];
       Result := SQLite3_Finalize(p);
       if Result <> SQLITE_OK then ShowError;
     end
   else
     ShowError;
 until PF^ = #0;
end;

function TRaSQLiteDB.SQLite3_PrepareResult(DB: Pointer; TheStatement: string; FParams: TParams; Sender: TObject): Pointer;
var
  i,tmpi: Integer;
  t: PAnsiChar;
  RV: Integer;
  colname, coltype: PChar;
  tmpcolname:string;
  FieldType: TFieldType;
  FieldLen: Integer;
  FieldDec: Integer;
  bFirst: Boolean;
  wildcard: Integer;
begin
  if not (Sender is TRaSQLiteBaseQuery) then Exit;

  with (Sender as TRaSQLiteBaseQuery) do begin
        RowId := -1;
        RowIdCol := -1;
        TheStatement := StringReplace(TheStatement, #2, '?', [rfReplaceAll, rfIgnoreCase]);

        bFirst := True; 
        if FUtf8 then
           RV := SQLite3_Prepare(DBHandle, PAnsiChar(AnsiToUtf8(TheStatement)), -1, result, t)
        else
           RV := SQLite3_Prepare(DBHandle, PAnsiChar(TheStatement), -1, result, t);

          wildcard := 1;
          if Assigned(FParams) then begin
              for i := 0 to FParams.Count - 1 do begin
                  if FParams[i].DataType in [ftBlob, ftGraphic] then begin
                      SQLite3_Bind_Blob(result, wildcard, PChar(FParams[i].AsBlob), FParams[i].GetDataSize, nil);
                      Inc(wildcard);
                    end;
                end;
            end;

          if RV <> 0 then ShowError else begin
              if bFirst then begin
                  bFirst := False;
                  if SQLite3_Column_count(result) > 0 then FieldDefs.Clear;
                  for i := 0 to SQLite3_Column_count(result) - 1 do begin
                      colname := SQLite3_Column_name(result, i);
                      if (FieldDefs.IndexOf(colname) >= 0) then begin           // Mirko
                         tmpColName := colname;                                 // Mirko
                         tmpI := 0;                                             // Mirko
                         while (FieldDefs.IndexOf(tmpcolname) >= 0) do begin    // Mirko
                           inc(tmpI);                                           // Mirko
                           tmpColName := colname + '_' + inttostr(tmpI);        // Mirko
                         end;                                                   // Mirko
                         colName := PChar(tmpColName);                          // Mirko
                      end;                                                      // Mirko

                      if CompareText(colname, 'rowid') = 0 then begin
                          RowIdCol := i;
                        end else begin
                          coltype := SQLite3_Column_decltype(result, i);
                          if coltype = nil then
                            GetFieldInfo('string', FieldType, FieldLen, FieldDec)
                          else
                            GetFieldInfo(coltype, FieldType, FieldLen, FieldDec);
                          if TypeLess then begin
                              FieldType := ftString;
                              with FieldDefs.AddFieldDef do begin
                                  Name := colname;
                                  DataType := FieldType;
                                  Size := FieldLen;
                                end;
                            end else begin
                              with FieldDefs.AddFieldDef do begin
                                  if FieldType <> ftString then begin
                                      Name := colname;
                                      DataType := FieldType;
                                      if FieldType = ftFloat then
                                        Precision := FieldDec;
                                    end else begin
                                      Name := colname;
                                      DataType := FieldType;
                                      Size := FieldLen;
                                    end;
                                end;
                            end;
                          MaxStrLen := MaxStrLen + GetNativeFieldSize(i + 1);
                          FResult.SetBufSize(MaxStrLen + 1 + SizeOf(TBookMark));
                        end;
                    end;
                end;
              end;
          end;
end;

function TRaSQLiteDB.SQLite_XExec(db: Pointer; SQLStatement: PAnsiChar; CallbackPtr: Pointer; Sender: TObject; var ErrMsg: PAnsiChar): integer;
begin

end;

function TRaSQLiteDB.SQLite3_GetNextResult(DB: Pointer; TheStatement: pointer; FParams: TParams; Sender: TObject) : pointer;
var
  i : integer;
  minmin : integer;
  RV: Integer;
  mv: Integer;
  convertbuf: TConvertBuffer;
  pData: PAnsiChar;
  BlobStream: TMemoryStream;
begin
  result := nil;
  with (Sender as TRaSQLiteBaseQuery) do begin

                FillChar(ResultStr^, MaxBuf, 0);
                RV := SQLite3_Step(theStatement);
                if RV = SQLITE_ROW then begin
                    if (Sender is TRaSQLiteQuery) then
                        MinMin := 1
                    else
                        MinMin := 1;
                    for i := 0 to SQLite3_Column_count(theStatement) - MinMin do begin
                        if i = RowIdCol then begin
                            RowId := SQLite3_Column_int(theStatement, i);
                          end else begin

                            pData := SQLite3_Column_text(theStatement, i);
                            if pData = nil then pData := '';
                            if FTypeLess then begin
                                mv := GetNativeFieldSize(i + 1);
                                if StrLen(pData) < Cardinal(mv) then
                                  mv := StrLen(pData);
                                Move(pData^, (ResultStr + GetFieldOffset(i + 1))^, mv);
                            end else begin
                                case FieldDefs[i].DataType of
                                  ftString:
                                    begin
                                      mv := GetNativeFieldSize(i + 1);
                                      if pData <> nil then
                                        begin
                                          if StrLen(pData) < Cardinal(mv) then
                                             mv := StrLen(pData)+1;
                                          Move(pData^, (ResultStr + GetFieldOffset(i + 1))^, mv);
                                        end;
                                    end;
                                  ftMemo, ftGraphic, ftFmtMemo, ftBlob:
                                    begin
                                      pData := SQLite3_Column_blob(theStatement, i);
                                      BlobStream := TMemoryStream.Create;
                                      if pData <> nil then
                                        BlobStream.Write(pData^, SQLite3_Column_bytes(theStatement, i))
                                      else begin
                                          pData := '';
                                          BlobStream.Write(pData^, 0);
                                        end;
                                      Move(BlobStream, (ResultStr + GetFieldOffset(i + 1))^, SizeOf(BlobStream));
                                    end;
                                else
                                  begin
                                    convertbuf := UnpackBuffer(pData, FieldDefs[i].DataType);
                                    Move(convertbuf, (ResultStr + GetFieldOffset(i + 1))^, GetFieldSize(i + 1));
                                  end;
                                end;
                            end;
                          end;
                      end;
                    Result := ResultStr;
                  end;
              if RV in [SQLITE_DONE] then result := nil;
              if RV in [SQLITE_ERROR, SQLITE_MISUSE] then
                ShowError;
            end;
end;

procedure TRaSQLiteDB.SQLite3_CloseResult(TheStatement : pointer);
var RV : integer;
begin
    if TheStatement <> nil then begin
       SQLite3_Reset(TheStatement);
       RV := SQLite3_Finalize(TheStatement);
       if RV <> 0 then raise RaSQLiteError.Create('SQLiteExecute error: ' + IntToStr(RV));
    end;
end;

function TRaSQLiteDB.SQLite3_Execute(DB: Pointer; TheStatement: string; FParams: TParams; Sender: TObject): Integer;
var
  p: Pointer;
  RowIdCol: Integer;
  Cursor: TDBScreenCursor; 
  keyError: Boolean;
begin
  SQLite3_Execute := 0;
  RowIdCol := -1;
  if not (Sender is TRaSQLiteBaseQuery) then Exit;
  try
    with (Sender as TRaSQLiteBaseQuery) do begin
     if Assigned(DBScreen) then begin
       Cursor := DBScreen.Cursor;
       DBScreen.Cursor := dcrSQLWait;
     end;
    RowId := -1;

    FStatement := Connection.SQLite3_PrepareResult(Connection.DBHandle, PAnsiChar(TheStatement),FParams, Sender);
    keyError := False;
    repeat
      try
       p := Connection.SQLite3_GetNextResult(Connection.DBHandle, FStatement, FParams, Sender);
       if p <> nil then
          FResult.Add(ResultStr, RowId);
      except
        keyError := True;
        Exit;

      end;
    until p = nil;
    Connection.SQLite3_CloseResult(FStatement);
    FStatement := nil;
    end;
  finally
    if Assigned(DBScreen) then
        DBScreen.Cursor := Cursor;
    if keyError then
      raise Exception.Create('Database logic error.');
  end;
end;

function TRaSQLiteDB.FGetDriverDLL: string;
begin
  if FDriverDLL = '' then
    FDriverDLL := 'SQLite3.dll';
  FGetDriverDLL := FDriverDLL;
end;

function TRaSQLiteDB.FGetDefaultExt: string;
begin
  if FDefaultExt = '' then
    FDefaultExt := '.db';
  FGetDefaultExt := FDefaultExt;
end;

procedure TRaSQLiteDB.FSetDatabase(Database: string);
begin
  FDatabase := Trim(Database);
  if ExtractFileExt(FDataBase)='' Then
     FDatabase:=FDataBase+FDefaultExt;
end;

procedure TRaSQLiteDB.ShowDatabases(List: TStrings);
var
  sr                : TSearchRec;
begin
  if DefaultExt = '' then
    DefaultExt := '.db';
  if DefaultExt[1] <> '.' then
    DefaultExt := '.' + DefaultExt;
  if DefaultDir <> '' then
    if DefaultDir[Length(DefaultDir)] <> '\' then
      DefaultDir := DefaultDir + '\';
  if FindFirst(FDefaultDir + '*' + DefaultExt, faAnyFile, sr) = 0 then
  begin
    repeat
      List.Add(sr.Name);
    until FindNext(sr) <> 0;
    SysUtils.FindClose(sr);
  end;
end;

procedure TRaSQLiteDB.GetTableNames(List: TStrings; SystemTables: boolean = false);
var
  ResultPtr         : Pointer;
  ResultStr         : ^Pointer;
  RowCount          : cardinal;
  ColCount          : cardinal;
  ErrMsg            : PAnsiChar;
  i                 : integer;
begin
  if not FConnected then
    Connected := true;
  if FConnected then
  begin
    SQLite3_GetTable(DBHandle, PAnsiChar(
      'SELECT name FROM sqlite_master WHERE type="table" ORDER BY name'),
      ResultPtr, RowCount, ColCount, ErrMsg);

    ResultStr := ResultPtr;
    List.Clear;
    Inc(ResultStr);
    for i := 1 to RowCount do
    begin
      if (CompareText('name', PAnsiChar(ResultStr^)) <> 0) then
        List.Add(PAnsiChar(ResultStr^));
      Inc(ResultStr);
    end;
    if Assigned(ResultPtr) then SQLite3_FreeTable(ResultPtr);
  end;
end;

procedure TRaSQLiteDB.GetIndexNames(List: TStrings; SystemTables: boolean = false);
var
  ResultPtr         : Pointer;
  ResultStr         : ^Pointer;
  RowCount          : cardinal;
  ColCount          : cardinal;
  ErrMsg            : PAnsiChar;
  i                 : integer;
begin
  if not FConnected then
    Connected := true;
  if FConnected then
  begin
    SQLite3_GetTable(DBHandle, PAnsiChar(
      'SELECT name FROM sqlite_master WHERE type="index" ORDER BY name'),
      ResultPtr, RowCount, ColCount, ErrMsg);

    ResultStr := ResultPtr;
    List.Clear;
    Inc(ResultStr);
    for i := 1 to RowCount do
    begin
      List.Add(PAnsiChar(ResultStr^));
      Inc(ResultStr);
    end;
    if Assigned(ResultPtr) then SQLite3_FreeTable(ResultPtr);
  end;
end;

procedure TRaSQLiteDB.GetFieldNames(TableName: string; List: TStrings);
var
  ResultPtr         : Pointer;
  ResultStr         : ^Pointer;
  RowCount          : cardinal;
  ColCount          : cardinal;
  ErrMsg            : PAnsiChar;
  i                 : integer;
begin
  if not FConnected then
    Connected := true;
  if FConnected then
  begin
    SQLite3_GetTable(DBHandle, PAnsiChar('PRAGMA table_info("' + TableName + '");'),
      ResultPtr, RowCount, ColCount, ErrMsg);
    ResultStr := ResultPtr;
    List.Clear;
    Inc(ResultStr, 6);
    for i := 1 to RowCount do
    begin
      Inc(ResultStr);
      List.Add(PAnsiChar(ResultStr^));
      Inc(ResultStr, 5);
    end;
    if Assigned(ResultPtr) then SQLite3_FreeTable(ResultPtr);
  end;
end;

procedure TRaSQLiteDB.GetPrimaryKeys(TableName: string; List: TStrings);
var
  ResultPtr         : Pointer;
  ResultStr         : ^Pointer;
  RowCount          : cardinal;
  ColCount          : cardinal;
  ErrMsg            : PAnsiChar;
  Temp              : string;
  i                 : integer;
begin
  if not FConnected then
    Connected := true;
  if FConnected then
  begin
    SQLite3_GetTable(DBHandle, PAnsiChar('PRAGMA table_info("' + TableName + '");'),
      ResultPtr, RowCount, ColCount, ErrMsg);
    ResultStr := ResultPtr;
    List.Clear;
    Inc(ResultStr, 6);
    for i := 1 to RowCount do
    begin
      Inc(ResultStr);
      Temp := PAnsiChar(ResultStr^);
      Inc(ResultStr, 4);
      if PAnsiChar(ResultStr^) = '1' then
        List.Add(Temp);
      Inc(ResultStr);
    end;
    if Assigned(ResultPtr) then SQLite3_FreeTable(ResultPtr);
  end;
end;

procedure TRaSQLiteDB.GetTableInfo(TableName: string; List: TList);
var
  ResultPtr         : Pointer;
  ResultStr         : ^Pointer;
  RowCount          : cardinal;
  ColCount          : cardinal;
  ErrMsg            : PAnsiChar;
  Field             : TRaSQLiteField;
  i                 : integer;
begin
  if not FConnected then
    Connected := true;
  if FConnected then
  begin
    SQLite3_GetTable(DBHandle, PAnsiChar('PRAGMA table_info("' + TableName + '");'),
      ResultPtr, RowCount, ColCount, ErrMsg);
    ResultStr := ResultPtr;
    while List.Count > 0 do
    begin
      TRaSQLiteField(List[0]).Free;
      List.Delete(0);
    end;
    List.Clear;

    Inc(ResultStr,6);
    for i := 1 to RowCount do
    begin
      Field := TRaSQLiteField.Create;
      with Field do
      begin
        FieldNumber := StrToIntX(PAnsiChar(ResultStr^));
        Inc(ResultStr);
        FieldName := PAnsiChar(ResultStr^);
        Inc(ResultStr);
        FieldType := PAnsiChar(ResultStr^);
        Inc(ResultStr);
        FieldNN := StrToIntX(PAnsiChar(ResultStr^));
        Inc(ResultStr);
        FieldDefault := PAnsiChar(ResultStr^);
        Inc(ResultStr);
        FieldPK := StrToIntX(PAnsiChar(ResultStr^));
        Inc(ResultStr);
      end;
      List.Add(Field);
    end;
    if Assigned(ResultPtr) then SQLite3_FreeTable(ResultPtr);
  end;
end;

function TRaSQLiteDB.GetUserVersion(database : string=''): integer;
var
  ResultPtr         : Pointer;
  ResultStr         : ^Pointer;
  RowCount          : cardinal;
  ColCount          : cardinal;
  ErrMsg            : PAnsiChar;
begin
  GetUserVersion := -1;
  if not FConnected then
    Connected := true;
  if FConnected then
  begin
    if database <> '' then database := database +'.';
    SQLite3_GetTable(DBHandle, PAnsiChar(
      'PRAGMA '+Database+'user_version'),
      ResultPtr, RowCount, ColCount, ErrMsg);
    ResultStr := ResultPtr;
    Inc(ResultStr);
    GetUserVersion := StrToIntX(PAnsiChar(ResultStr^));
  end;
end;

procedure TRaSQLiteDB.SetUserVersion(Version : integer; Database : string='');
begin
  if Database <> '' then Database := Database +'.';
  SQLite3_ExecSQL('PRAGMA '+Database+'user_version='+IntToStr(Version));
end;

procedure TRaSQLiteDB.GetTableIndexNames(TableName: string; List: TStrings);
var
  ResultPtr         : Pointer;
  ResultStr         : ^Pointer;
  RowCount          : cardinal;
  ColCount          : cardinal;
  ErrMsg            : PAnsiChar;
  i                 : integer;
begin
  if not FConnected then
    Connected := true;
  if FConnected then
  begin
    SQLite3_GetTable(DBHandle, PAnsiChar(
      'PRAGMA  index_list("' + TableName + '");'),
      ResultPtr, RowCount, ColCount, ErrMsg);

    ResultStr := ResultPtr;
    List.Clear;
    Inc(ResultStr, 4);
    for i := 1 to RowCount do
    begin
      List.Insert(0, PAnsiChar(ResultStr^));
      Inc(ResultStr, 3);
    end;
    if Assigned(ResultPtr) then SQLite3_FreeTable(ResultPtr);
  end;
end;

procedure TRaSQLiteDB.DBConnect(Connected: boolean);
var
  ErrMsg            : PAnsiChar;
  DBMS              : string;
  rv                : integer;
  i                 : integer;
begin
  if (CompareText(FCharEnc,'utf8')=0) or (FCharEnc='') then
     FUtf8 := true
  else
     FUtf8 := false;

  if (Connected) and (FDatabase = '') then
  begin
    raise RaSQLiteError.Create('Missing database property');
    SQLite3_FreeMem(ErrMsg);
    FConnected := false;
    exit;
  end;

  if not Connected then
  begin
    if FConnected then
    begin
      if DLLHandle <> 0 then
      begin
        OutputDebugString('freeing sqlite dll');
        if Assigned(FBeforeDisconnect) then
          FBeforeDisconnect(self);
          if Assigned(Owner) Then
            For I:=0 to Owner.ComponentCount-1 do
              if Owner.Components[I] is TRaSQLiteBaseQuery Then
                TRaSQLiteBaseQuery(Owner.Components[I]).Active:=False;

        if Assigned(@SQLite3_Close) then
          SQLite3_Close(DBHandle); 
          FreeLibrary(DLLHandle);

        DLLHandle := 0;
        if Assigned(FAfterDisconnect) then
          FAfterDisconnect(self);
      end;
      FConnected := false;
      exit;
    end
  end
  else
  begin
    if CompareText(':memory:', Database) <> 0 then begin
       if DefaultDir <> '' then begin
         if DefaultDir[Length(DefaultDir)] <> '\' then
           DefaultDir := DefaultDir + '\';
         DBMS := DefaultDir + Database;
       end else begin
         if Pos('\', Database) = 0 then
           DBMS := GetCurrentDir + '\' + DataBase
         else
           DBMS := Database;
       end;

       if FMustExist then begin
         if not FileExists(DBMS) then begin
          raise EDatabaseError.Create('Database ' + DBMS + ' does not exist');
         end;
      end;
    end else DBMS := Database;

    if DLLHandle = 0 then
    begin
      if not LoadLibs then
      begin
        FConnected := false;
        raise RaSQLiteError.Create('Could not load SQLite library');
      end;
    end;

    FConnected := true;
    FVersion := SQLite3_LibVersion;

    DBHandle := nil;
    ErrMsg := nil;
    if Assigned(FBeforeConnect) then
      FBeforeConnect(self);
    if Assigned(@SQLite3_Open) then
    begin
      if FUTF8DBName then
        DBMS := AnsiToUtf8(DBMS);
      rv := SQLite3_Open(PAnsiChar(DBMS), DBHandle);
      if FPassword <> '' then
      begin
        if not CheckPassword then
        begin
          SQLite3_Close(DBHandle);
          raise Exception.Create('Password Error!');
        end;
        try
          SQLite3_ExecSQL('PRAGMA SYNCHRONOUS=NORMAL;');
        except
          raise Exception.Create('Password Error!');
        end;

      end
      else
      begin
        try
          SQLite3_ExecSQL('PRAGMA SYNCHRONOUS=NORMAL;');
        except
          raise Exception.Create('Password error or invalid database file.'); 
        end;  
      end;  
    end;

    if Assigned(FAfterConnect) then
      FAfterConnect(self);
    if DBHandle = nil then
      FConnected := false;

    if Assigned(FSQLitePragma) then
      ExecPragma;

    FLastError := ErrMsg;
    if ErrMsg <> nil then
      SQLite3_FreeMem(ErrMsg);
    if ExecuteInlineSQL and Assigned(FInlineSQL) then Try
      ExecStartTransaction('');
      SQLite3_ExecSQL(FInlineSQL.FSQL.Text);
      Commit;
    finally
      ExecuteInlineSQL:=False;
    end;
  end;
end;

function TRaSQLiteDB.RowsAffected: integer;
begin
  if not FConnected then
    Result := -1
  else
    Result := SQLite3_Changes(DBHandle);
end;

function TableExistsCallback(UserData: Pointer; ColumnCount: Integer; ColumnValues, ColumnNames: PPointer): Integer; cdecl;
begin
  if AnsiStrIComp(UserData, ColumnValues^) <> 0 then
    Result := 0
  else
    Result := 1;
end;

function TRaSQLiteDB.TableExists(const ATableName: AnsiString): Boolean;
var
  ErrMsg: PAnsiChar;
begin
  try
    Result := SQLite3_Exec(DBHandle, 'SELECT name FROM sqlite_master',
                           TableExistsCallback,Pointer(ATableName), ErrMsg) = SQLITE_ABORT;
  finally
    if ErrMsg <> nil then
      begin
        SQLite3_FreeMem(ErrMsg);
        ShowError;
      end;
  end;
end;

procedure TRaSQLiteDB.ExecStartTransaction(TransType: string);
begin
  if (TransType = '') then TransType := FTransactionType;

  if ((TransType = '') or (CompareText(TransType, 'DEFAULT') = 0)) then StartTransaction
  else if (CompareText(TransType, 'DEFERRED') = 0) then StartDeferredTransaction
  else if (CompareText(TransType, 'IMMEDIATE') = 0) then StartImmediateTransaction
  else if (CompareText(TransType, 'EXCLUSIVE') = 0) then StartExclusiveTransaction
  else StartTransaction;
end;

procedure TRaSQLiteDB.StartTransaction;
begin
  if not FConnected then
    Connected := true;
  if FConnected then SQLite3_ExecSQL('begin transaction');
end;

procedure TRaSQLiteDB.StartDeferredTransaction;
begin
  if not FConnected then
    Connected := true;
  if FConnected then SQLite3_ExecSQL('begin deferred transaction');
end;

procedure TRaSQLiteDB.StartImmediateTransaction;
begin
  if not FConnected then
    Connected := true;
  if FConnected then SQLite3_ExecSQL('begin immediate transaction');
end;

procedure TRaSQLiteDB.StartExclusiveTransaction;
begin
  if not FConnected then
    Connected := true;
  if FConnected then SQLite3_ExecSQL('begin exclusive transaction');
end;

procedure TRaSQLiteDB.Open;
begin
  Connected := true;
  if DLLHandle = 0 then
    Connected := false;

end;

procedure TRaSQLiteDB.Close;
begin
  Connected := false;

end;

procedure TRaSQLiteDB.ExecPragma;
var
  Cmd               : string;
begin
  if not FConnected then
    Connected := true;
  if FConnected then
  begin
    if FSQLitePragma.FTempCacheSize <> 0 then
    begin
      cmd := FSQLitePragma.GetTempCacheSize;
      SQLite3_ExecSQL(cmd);
    end;
    if FSQLitePragma.FDefaultCacheSize <> 0 then
    begin
      cmd := FSQLitePragma.GetDefaultCacheSize;
      SQLite3_ExecSQL(cmd);
    end;

    if FSQLitePragma.FDefaultSynchronous <> '' then
    begin
      cmd := FSQLitePragma.GetDefaultSynchronous;
      SQLite3_ExecSQL(cmd);
    end;

    if FSQLitePragma.FDefaultTempStore <> '' then
    begin
      cmd := FSQLitePragma.GetDefaultTempStore;
      SQLite3_ExecSQL(cmd);
    end;

    if FSQLitePragma.FTempStore <> '' then
    begin
      cmd := FSQLitePragma.GetTempStore;
      SQLite3_ExecSQL(cmd);
    end;

    if FSQLitePragma.FSynchronous <> '' then
    begin
      cmd := FSQLitePragma.GetSynchronous;
      SQLite3_ExecSQL(cmd);
    end;
  end;
end;

procedure TRaSQLiteDB.Commit;
begin
  if not FConnected then
    Connected := true;
  if FConnected then SQLite3_ExecSQL('commit transaction');
end;

procedure TRaSQLiteDB.RollBack;
begin
  if not FConnected then
    Connected := true;
  if FConnected then
    SQLite3_ExecSQL('rollback transaction');
end;

constructor TRaSQLiteDB.Create(AOwner: TComponent);
begin
  Connected := false;
  SQLitePragma := nil;
  inherited Create(AOwner);
end;

destructor TRaSQLiteDB.Destroy;
begin
  FConnected := false;
  SQLitePragma := nil;
  inherited Destroy;
end;

function TRaSQLiteBaseQuery.CalcFieldInList(const List: string): Boolean;
var i: Integer;
    Fields: TList;
begin
  if Pos(';', List) <> 0 then
  begin
    Result := False;
    Fields := TList.Create;
    try
      GetFieldList(Fields, List);
      for i := 0 to Fields.Count - 1 do
        if TField(Fields[I]).FieldKind in [fkCalculated, fkLookup] then Result := True;
    finally
      Fields.Free;
    end;
  end else
    Result := (FieldByName(List).FieldKind in [fkCalculated, fkLookup]);
end;

function TRaSQLiteBaseQuery.Lookup(const KeyFields: string; const KeyValues: Variant;
  const ResultFields: string): Variant;
var OldState: TDataSetState;
begin
  Result := '';
  if Locate(KeyFields, KeyValues, []) then
  begin
    if CalcFieldInList(ResultFields) then GetCalcFields(PChar(FResult.GetData(FCurRec)));
    OldState := SetTempState(dsFilter);
    try
      Result := FieldValues[ResultFields];
    finally
      RestoreState(OldState);
    end;
  end;
end;

procedure TRaSQLiteBaseQuery.DataConvert(Field: TField; Source, Dest: Pointer;
  ToNative: Boolean);
const x: Word = 0;
var L: Integer;
begin
  try
    case Field.DataType of
     ftWideString:
       begin
        if ToNative then
          begin
           L := Length(PWideChar(Source^));
           if (L <= 0) then
            Move(x,Dest^,2)
           else
            begin
             if (L <= Field.Size) then
              Move(PWideChar(Source^)^, Dest^, (L+1)*2)
             else
              begin
               Move(PWideChar(Source^)^, Dest^, Field.Size*2);
               Move(x, (PChar(Dest)+Field.Size*2 - 2)^, 2); 
              end;
            end;
          end
         else
          begin
           WideString(Dest^) := WideString(PWideChar(Source));
          end;
       end;
     else
      begin
       inherited DataConvert(Field, Source, Dest, ToNative);
      end
    end;
  finally
  end;
end;

procedure TRaSQLiteBaseQuery.RegisterDetailDataset(DetailDataSet: TRaSQLiteBaseQuery);
var
  i                 : integer;
begin
  try
    for i := 0 to DetailList.Count - 1 do
      if DetailList[i] = DetailDataset then exit;
    DetailList.Add(DetailDataSet);
  finally
  end;
end;

procedure TRaSQLiteBaseQuery.SQLiteMasterChanged;
begin

end;

procedure TRaSQLiteBaseQuery.NotifySQLiteMasterChanged;
var
  i                 : integer;
begin
  for i := 0 to DetailList.Count - 1 do
  begin
    TRaSQLiteBaseQuery(DetailList[i]).SQLiteMasterChanged;
  end;

end;

function TRaSQLiteBaseQuery.UnpackBuffer(Buffer: PAnsiChar; FieldType: TFieldType): TConvertBuffer;
var
  TempInt           : integer;
  TempDouble        : double;
  TempBool          : wordbool;
  TempT             : TDateTimeRec;
begin
  case FieldType of
    ftString:
      begin
        exit;
      end;
    ftInteger, ftSmallInt:
      begin
        TempInt := StrToIntX(Buffer);
        Move(TempInt, result, sizeof(TempInt));
      end;
    ftTime:
      begin
        TempT := DateTimeToNative(FieldType, StrToDateTimeX(Buffer));
        Move(TempT, result, sizeof(TDateTime));
      end;
    ftDate:
      begin
        TempT := DateTimeToNative(FieldType, StrToDateTimeX(Buffer));
        Move(TempT, result, sizeof(TDateTime));
      end;
    ftDateTime:
      begin
        if FSQLiteDateFormat then 
          TempT := DateTimeToNative(FieldType, YYYYMMDDParser(Buffer))
        else
          TempT := DateTimeToNative(FieldType, StrToDateTimeX(Buffer));
        Move(TempT, result, sizeof(TDateTime));
      end;
    ftFloat, ftBCD, ftCurrency:
      begin
        TempDouble := StrToFloatX(FloatParser(Buffer));
        Move(TempDouble, result, sizeof(TempDouble));
      end;

    ftBoolean:
      begin
        TempBool := StrToBool(Buffer);
        Move(TempBool, result, sizeof(TempBool));
      end;
    ftMemo, ftGraphic, ftBlob, ftFMTMemo:
      begin
        TempInt := StrToInt(Buffer);
        Move(TempInt, result, sizeof(TempInt));
      end;
  end;
end;

constructor TRaSQLiteBaseQuery.Create(AOwner: TComponent);
begin
  MaxStrLen := 0;
  FSQL := TStringList.Create;
  FParams := TParams.Create(Self);
  DetailList := TList.Create;
  FConnection := nil;
  FResult := nil;
  GetMem(ResultStr, MaxBuf);
  SQLCursor := true;
  SQLiteDateFormat := true;
  TypeLess := false;
  ReadOnly := false;
  inherited;
end;

function TRaSQLiteBaseQuery.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
begin
  Result := TRaSQLiteBlobStream.Create(Field as TBlobField, Mode);
end;

destructor TRaSQLiteBaseQuery.Destroy;
begin
  if Assigned(FSQL) then begin
    TStringList(FSQL).OnChange := nil;
    FSQL.Free;
  end;
  FSQL := nil;

  if Assigned(FParams) then
  begin
    FParams.Free;
    FParams := nil;
  end;

  if Assigned(DetailList) then
    DetailList.Free;
  DetailList := nil;

  if Assigned(FConnection) then
    FConnection := nil;

  if Assigned(ResultStr) then
    FreeMem(ResultStr);
  ResultStr := nil;

  if Assigned(FResult) then
    FResult.Free;
  FResult := nil;

  inherited;
end;

procedure TRaSQLiteBaseQuery.StartTransaction;
begin
  if Assigned(FConnection) then
    FConnection.ExecStartTransaction(FTransActionType);
end;

procedure TRaSQLiteBaseQuery.StartDeferredTransaction;
begin
  if Assigned(FConnection) then
    FConnection.StartDeferredTransaction;
end;

procedure TRaSQLiteBaseQuery.StartImmediateTransaction;
begin
  if Assigned(FConnection) then
    FConnection.StartImmediateTransaction;
end;

procedure TRaSQLiteBaseQuery.StartExclusiveTransaction;
begin
  if Assigned(FConnection) then
    FConnection.StartExclusiveTransaction;;
end;

procedure TRaSQLiteBaseQuery.Commit;
begin
  if Assigned(FConnection) then
    FConnection.Commit;
end;

procedure TRaSQLiteBaseQuery.RollBack;
begin
  if Assigned(FConnection) then
    FConnection.RollBack;
end;

function TRaSQLiteBaseQuery.Locate(const KeyFields: string;
  const KeyValues: variant; Options: TLocateOptions): boolean;
var
  bOk               : boolean;
  i, j, p           : integer;
  Fields, SearchValue: string;
  FieldList         : TStringList;
  DebugStr          : string;
  DoEnableControls  : boolean;
begin
  DoEnableControls := not ControlsDisabled;
  DisableControls; 
  FieldList := TStringList.Create;
  bOk := false;
  try
    Fields := KeyFields;
    p := pos(';', Fields);
    while p > 0 do
    begin
      FieldList.Add(Copy(Fields, 1, p - 1));
      System.Delete(Fields, 1, p);
      p := pos(';', Fields);
    end;
    if Fields <> '' then
      FieldList.Add(Fields);

    First;
    for i := 1 to FResult.Data.Count do
    begin
      SetRecNo(i);
      bOk := true;
      for j := 0 to FieldList.Count - 1 do
      begin
        if loCaseInsensitive in Options then
        begin
          if FieldList.Count = 1 then
          begin
            SearchValue := VarToStr(KeyValues);
            if (loPartialKey in Options) and (Length(SearchValue) <= Length(FieldByName(FieldList[j]).AsString)) then
              bOk := CompareText(Copy(FieldByName(FieldList[j]).AsString, 1, Length(SearchValue)), SearchValue) = 0
            else
              bOk := CompareText(FieldByName(FieldList[j]).AsString, SearchValue) = 0
          end
          else
          begin
            SearchValue := VarToStr(KeyValues[j]);
            if (loPartialKey in Options) and (Length(SearchValue) <= Length(FieldByName(FieldList[j]).AsString)) then
              bOk := CompareText(Copy(FieldByName(FieldList[j]).AsString, 1, Length(SearchValue)), SearchValue) = 0
            else
              bOk := CompareText(FieldByName(FieldList[j]).AsString, SearchValue) = 0
          end
        end
        else
        begin
          if FieldList.Count = 1 then
          begin
            SearchValue := VarToStr(KeyValues);
            if (loPartialKey in Options) and (Length(SearchValue) <= Length(FieldByName(FieldList[j]).AsString)) then
              bOk := Copy(FieldByName(FieldList[j]).AsString, 1, Length(SearchValue)) = SearchValue
            else
              bOk := FieldByName(FieldList[j]).AsString = SearchValue
          end
          else
          begin
            SearchValue := VarToStr(KeyValues[j]);
            if (loPartialKey in Options) and (Length(SearchValue) <= Length(FieldByName(FieldList[j]).AsString)) then
              bOk := Copy(FieldByName(FieldList[j]).AsString, 1, Length(SearchValue)) = SearchValue
            else
              bOk := FieldByName(FieldList[j]).AsString = SearchValue
          end;
        end;
        if bOk = false then
          break;
      end;
      if bOk then
      begin
        break;
      end;
    end;
    if bOk then
    begin
      Locate := true;
      DebugStr := 'TRaSQLiteBaseQuery.Locate true';
    end
    else
    begin
      Locate := false;
      DebugStr := 'TRaSQLiteBaseQuery.Locate false';
    end;
  finally
    FieldList.Free;
    if DoEnableControls then
      EnableControls;
  end;
end;

function TRaSQLiteBaseQuery.LocateNearest(const KeyFields: String; const KeyValues: Variant; Options: TLocateOptions): Boolean;
begin

end;

function TRaSQLiteBaseQuery.GetDataSource: TDataSource;
begin
  Result := FMasterSource;
end;

procedure TRaSQLiteBaseQuery.SetSQLiteDateFormat(const Value: boolean);
begin
  FSQLiteDateFormat := Value;
end;

procedure TRaSQLiteBaseQuery.SetDataSource(Value: TDataSource); 
begin
  if IsLinkedTo(Value) then
    DatabaseError('circular references are not allowed', Self);
  FMasterSource := Value;
end;

function TRaSQLiteBaseQuery.GetMasterFields: string;
begin
  Result := FMasterFields;
end;

procedure TRaSQLiteBaseQuery.SetMasterFields(const Value: string);
begin
  FMasterFields := Value;
end;

function TRaSQLiteBaseQuery.GetActiveBuffer(var Buffer: PAnsiChar): boolean;
begin
  case State of
    dsBrowse: if IsEmpty then
        Buffer := nil
      else
        Buffer := ActiveBuffer;

    dsEdit: Buffer := ActiveBuffer;
    dsInsert: Buffer := ActiveBuffer;
    dsFilter: Buffer := ActiveBuffer;
    dsCalcFields: Buffer := CalcBuffer;
  else
    Buffer := nil;
  end;
  Result := Buffer <> nil;
end;

function TRaSQLiteBaseQuery.GetNativeFieldSize(FieldNo: integer): integer;
begin
  Result := 0;
  case FieldDefs.Items[FieldNo - 1].Datatype of
    ftString: Result := FieldDefs.Items[FieldNo - 1].Size + 1;
    ftWideString: Result := FieldDefs.Items[FieldNo - 1].Size + 1;    
    ftInteger, ftSmallInt, ftDate, ftTime: Result := 12;
    ftDateTime: Result := 20;
    ftFloat, ftBCD, ftCurrency: Result := 12;
    ftBoolean: Result := 12;
    ftGraphic, ftMemo, ftBlob, ftFmtMemo: Result := 12;
  else
    raise RaSqliteError.Create('Fieldtype of Field "' + FieldDefs.Items[FieldNo - 1].Name +
      '" not supported!');
  end;
end;

function TRaSQLiteBaseQuery.GetFieldSize(FieldNo: integer): integer;
begin
  Result := 0;
  case FieldDefs.Items[FieldNo - 1].Datatype of
    ftString: Result := FieldDefs.Items[FieldNo - 1].Size+ 1 ;
    ftWideString: Result := FieldDefs.Items[FieldNo - 1].Size+ 1 ;  
    ftInteger, ftSmallInt, ftDate, ftTime: Inc(Result, sizeof(integer));
    ftDateTime: Inc(Result, sizeof(TDateTime));
    ftFloat, ftBCD, ftCurrency: Inc(Result, sizeof(double));
    ftBoolean: Inc(Result, sizeof(wordbool));
    ftGraphic, ftMemo, ftBlob, ftFmtMemo: Inc(Result, sizeof(pointer));
  else
    raise RaSqliteError.Create('Fieldtype of Field "' + FieldDefs.Items[FieldNo - 1].Name +
      '" not supported!');
  end;
end;

function TRaSQLiteBaseQuery.GetFieldSize(Field: TField): integer;
begin
  Result := 0;
  case Field.DataType of
    ftString: Result := Field.Size + 1;
    ftWideString: Result := Field.Size + 1;
    ftInteger, ftSmallInt, ftDate, ftTime: Inc(Result, sizeof(integer));
    ftDateTime: Inc(Result, sizeof(TDateTime));
    ftFloat, ftBCD, ftCurrency: Inc(Result, sizeof(double));
    ftBoolean: Inc(Result, sizeof(wordbool));
    ftGraphic, ftMemo, ftBlob, ftFmtMemo: Inc(Result, sizeof(pointer));
  else
    raise RaSqliteError.Create('Fieldtype of Field "' + Field.FieldName +
      '" not supported!');
  end;
end;

function TRaSQLiteBaseQuery.GetFieldOffset(FieldNo: integer): integer;
var
  i                 : integer;
  Offset            : integer;
begin
  Offset := 0;
  if FieldNo > 1 then
  begin
    for i := 1 to FieldNo - 1 do
      OffSet := OffSet + GetFieldSize(i);
  end;
  GetFieldOffset := Offset;
end;

function TRaSQLiteBaseQuery.GetCalcFieldOffset(Field: TField): integer;
var
  i                 : integer;
  Offset            : integer;
begin
  Offset := FRecBufSize + sizeof(TRecInfo);
  for i := 0 to FieldList.Count - 1 do begin
    if CompareText(FieldList[i].FieldName, Field.FieldName) = 0 then begin
      GetCalcFieldOffset := Offset;
      exit;
    end;
    if FieldList[i].Calculated then
      OffSet := OffSet + GetFieldSize(Field);
  end;
  GetCalcFieldOffset := Offset;
end;

procedure TRaSQLiteBaseQuery.SetSQL(const Value: TStrings);
begin
  Close;
  if Assigned(FSQL) then
    FSQL.Assign(Value)
  else
    FSQL := Value;
end;

procedure TRaSQLiteBaseQuery.LoadQueryData;
var
  res: integer;
begin
  if Connection.FConnected then
  begin
    Connection.SQLite3_execute(Connection.DBHandle, PAnsiChar(FPrepared), FParams, self);
  end;
end;

procedure TRaSQLiteBaseQuery.InternalOpen;
begin
  if UniDirectional then
     SetUnidirectional(true)
  else
     SetUniDirectional(false);
     
  MaxStrLen := 0;
  if (Connection = nil) then
  begin
    raise RaSqliteError.Create('no database connection');
  end
  else
  begin

    if Connection.Connected = false then
      Connection.Connected := true;

    if (Connection.Connected) and (Connection.DLLHandle <> 0) then
      if Assigned(MasterSource) then
      begin
        if (MasterSource.DataSet <> nil) then
        begin
          if CompareText(Copy(MasterSource.DataSet.ClassName, 1, 9), 'TRaSQLite') = 0 then
          begin
            TRaSQLiteBaseQuery(MasterSource.DataSet).RegisterDetailDataset(
              TRaSQLiteBaseQuery(Self));
          end
          else
          begin
            raise RaSqliteError.Create('master dataset ' + MasterSource.DataSet.ClassName +
              ' is not of TSQLiteBaseQuery type');
            exit;
          end;
        end
        else
        begin
          raise RaSqliteError.Create('master dataset undefined');
          exit;
        end;
      end;
    if not FUniDir then begin
       FResult := TFResult.Create(Self);
       LoadQueryData;
    end else begin
       FResult := TFResult.Create(Self);
       FStatement := Connection.SQLite3_PrepareResult(Connection.DBHandle, PAnsiChar(FPrepared),FParams, self);
    end;
    FCurRec := -1;
    FRecInfoOfs := MaxStrLen;
    FRecBufSize := FRecInfoOfs + SizeOf(TRecInfo);
    BookmarkSize := SizeOf(integer); 
    InternalInitFieldDefs;
    if DefaultFields then
      CreateFields;
    BindFields(true);

  end;
end;

procedure TRaSQLiteBaseQuery.InternalClose;
begin
   if (FUniDir) and (FStatement <> nil) and (active) then begin
      Connection.SQLite3_CloseResult(FStatement);
      FStatement := nil;
   end;

  if Assigned(FResult) then
  begin
    FResult.Free;
    FResult := nil;
  end;
  if DefaultFields then
    DestroyFields;
  FCurRec := -1;
end;

function TRaSQLiteBaseQuery.IsCursorOpen: boolean;
begin
  Result := Assigned(FResult);
end;

procedure TRaSQLiteBaseQuery.OpenCursor(InfoQuery: Boolean);
begin  
  if InfoQuery then  
    Begin   
      if Assigned(FConnection) Then Begin  
        InternalOpen;
        InternalClose;  
      End;  
    End  
  else if State <> dsOpening then  
    inherited OpenCursor(InfoQuery);  
end;  

procedure TRaSQLiteBaseQuery.InternalInitFieldDefs;
begin

end;

procedure TRaSQLiteBaseQuery.InternalHandleException;
begin
  ApplicationHandleException(Self);
end;

procedure TRaSQLiteBaseQuery.InternalGotoBookmark(Bookmark: Pointer);
var
  Index             : integer;
begin
  Index := FResult.IndexOf(TObject(PInteger(Bookmark)^));
  if Index <> -1 then
    FCurRec := Index
  else
    if not FUniDir then DatabaseError('Bookmark not found');
end;

function TRaSQLiteBaseQuery.BookmarkValid(Bookmark: Pointer): boolean;
var
  Index             : integer;
begin
  Index := FResult.IndexOf(TObject(PInteger(Bookmark)^));
  if Index <> -1 then
    BookmarkValid := true
  else
    BookmarkValid := false;
end;

procedure TRaSQLiteBaseQuery.InternalSetToRecord(Buffer: PAnsiChar);
begin
  InternalGotoBookmark(@PRecInfo(Buffer + FRecInfoOfs).Bookmark);
end;

function TRaSQLiteBaseQuery.GetBookmarkFlag(Buffer: PAnsiChar): TBookmarkFlag;
begin
  Result := PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag;
end;

procedure TRaSQLiteBaseQuery.SetBookmarkFlag(Buffer: PAnsiChar; Value: TBookmarkFlag);
begin
  PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag := Value;
end;

procedure TRaSQLiteBaseQuery.GetBookmarkData(Buffer: PAnsiChar; Data: Pointer);
begin
  PInteger(Data)^ := PRecInfo(Buffer + FRecInfoOfs).Bookmark;
end;

procedure TRaSQLiteBaseQuery.SetBookmarkData(Buffer: PAnsiChar; Data: Pointer);
begin
  PRecInfo(Buffer + FRecInfoOfs).Bookmark := PInteger(Data)^;
end;

function TRaSQLiteBaseQuery.GetRecordSize: word;
begin
  Result := MaxStrLen;
end;

function TRaSQLiteBaseQuery.AllocRecordBuffer: PAnsiChar;
begin
  GetMem(Result, FRecBufSize + CalcFieldsSize + sizeof(TRecinfo) + 5);
  FillChar(Result^, FRecBufSize + CalcFieldsSize + sizeof(TRecinfo) + 5, 0);
end;

procedure TRaSQLiteBaseQuery.FreeRecordBuffer(var Buffer: PAnsiChar);
begin
  try FreeMem(Buffer);
  except end;
end;

function TRaSQLiteBaseQuery.GetRecord(Buffer: PAnsiChar; GetMode: TGetMode;
  DoCheck: boolean): TGetResult;
var
  ptr               : pointer;
begin
  if (not (FUniDir)) and (FResult.Count < 1) then
    Result := grEOF
  else
  begin
    Result := grOK;
    case GetMode of
      gmNext:
        if FUniDir then begin
        end else begin
           if FCurRec >= RecordCount - 1 then
              Result := grEOF
           else
             Inc(FCurRec);
        end;
      gmPrior: begin
                if FUniDir then
                   raise RaSqliteError.Create('operation PRIOR not allowed on unidirectional dataset')
                else begin
                   if FCurRec <= 0 then
                      Result := grBOF
                   else
                      Dec(FCurRec);
                   end;
               end;
      gmCurrent:
        begin
          if (FCurRec < 0) or (FCurRec >= RecordCount) then
            Result := grError;
        end;
    end;
    if Result = grOK then
    begin
      if FUniDir then begin
          ptr := Connection.SQLite3_GetNextResult(Connection.DBHandle, FStatement, FParams,self);
          if ptr <> nil then begin
             Move(ptr^, Buffer^, MaxStrLen);
          end else Result := grEOF;
      end else begin
          ptr := FResult.GetData(FCurRec);
          if FResult.Count = 0 then
             InternalInitRecord(Buffer)
          else
             if ptr <> nil then Move(ptr^, Buffer^, MaxStrLen);
      end;

      with PRecInfo(Buffer + FRecInfoOfs)^ do
      begin
        BookmarkFlag := bfCurrent;
        Bookmark := FResult.GetBookMark(FCurRec);
      end;

      if CalcFieldsSize > 0 then
        GetCalcFields(Buffer)

    end
    else if (Result = grError) and DoCheck then
      DatabaseError('No Records');
  end;
end;

procedure TRaSQLiteBaseQuery.InternalInitRecord(Buffer: PAnsiChar);
var
  i                 : integer;
  TempT             : TDateTimeRec;
  Stream            : TMemoryStream;
begin
  for i := 0 to FieldDefs.Count - 1 do
  begin
    if not (Fields[i].Calculated) then begin
      case FieldDefs.Items[i].Datatype of
        ftMemo, ftGraphic, ftBlob, ftFmtMemo: begin
            Stream := TMemoryStream.Create;
            Move(Pointer(Stream), (Buffer + GetFieldOffset(i + 1))^, sizeof(Pointer));
          end;
        ftString: PAnsiChar(Buffer + GetFieldOffset(i + 1))^ := #0;
        ftBoolean: pBoolean(Buffer + GetFieldOffset(i + 1))^ := false;
        ftFloat: pFloat(Buffer + GetFieldOffset(i + 1))^ := 0;
        ftSmallInt: pSmallInt(Buffer + GetFieldOffset(i + 1))^ := 0;
        ftInteger: pInteger(Buffer + GetFieldOffset(i + 1))^ := integer(nil);
        ftCurrency: pFloat(Buffer + GetFieldOffset(i + 1))^ := 0;
        ftDate:
          begin
            TempT := DateTimeToNative(ftDate, now);
            Move(TempT, (Buffer + GetFieldOffset(i + 1))^, sizeof(TDateTime));
          end;
        ftTime:
          begin
            TempT := DateTimeToNative(ftTime, now);
            Move(TempT, (Buffer + GetFieldOffset(i + 1))^, sizeof(TDateTime));
          end;
        ftDateTime:
          begin
            TempT := DateTimeToNative(ftDateTime, now);
            Move(TempT, (Buffer + GetFieldOffset(i + 1))^, sizeof(TDateTime));
          end;
      end;
    end;
  end;
end;

function TRaSQLiteBaseQuery.GetFieldData(Field: TField; Buffer: Pointer): boolean;
var
  MyBuf : string;
  SrcBuffer         : PAnsiChar;
begin
  if Field.FieldNo > 0 then begin
    if pos(UpperCase(Field.FieldName), UpperCase(FMasterFields)) > 0 then begin
       MasterSource.DataSet.GetFieldData(Field, Buffer);
       Result := true;
       exit;
    end;

    Result := true;
    if GetActiveBuffer(SrcBuffer) then begin
      if (Assigned(Buffer)) and (Assigned(SrcBuffer)) then begin
        Move((SrcBuffer + GetFieldOffset(Field.FieldNo))^, Buffer^, GetFieldSize(Field.FieldNo));
        if Field.DataType = ftString then begin
           MyBuf := PChar(Buffer);
           if Connection.FUtf8 then
              Move(Utf8ToAnsi(MyBuf)[1], Buffer^, Length(MyBuf))
           else
              Move(MyBuf[1], Buffer^, Length(MyBuf));
            PAnsiChar(PAnsiChar(Buffer) + GetFieldSize(Field.FieldNo))^ := #0;
        end;
        Result := true;
        exit;
      end;
      if Assigned(SrcBuffer) then
        if (Field.DataType <> ftDateTime) and ((SrcBuffer + GetFieldOffset(Field.FieldNo))^ = #0) then
          Result := false
    end else begin
      if assigned(Buffer) then PAnsiChar(Buffer)^ := #0;
      Result := false;
    end;
  end else begin
    Result := GetActiveBuffer(SrcBuffer);
    if Result and (State in [dsBrowse, dsEdit, dsInsert, dsCalcFields, dsBlockRead]) then begin
      if (Assigned(Buffer)) then
        Move((SrcBuffer + GetCalcFieldOffset(Field))^, Buffer^, GetFieldSize(Field));
    end;
  end;
end;

function TRaSQLiteBaseQuery.GetFieldData(FieldNo: integer; Buffer: Pointer): boolean;
begin
  Result := GetFieldData(FieldByNumber(FieldNo), Buffer);
end;

function TRaSQLiteBaseQuery.GetFieldData(Field: TField; Buffer: Pointer;
  NativeFormat: boolean): boolean;
begin
  Result := GetFieldData(Field, Buffer);
end;

procedure TRaSQLiteBaseQuery.SetFieldData(Field: TField; Buffer: Pointer);
var
  DestBuffer        : PAnsiChar;
  MyBuf             : string;
begin
  GetActiveBuffer(DestBuffer);
  if (Field.FieldNo > 0) and (Assigned(Buffer)) and (Assigned(DestBuffer)) then
  begin
    if Field.DataType = ftString then
      Begin
        MyBuf := PChar(Buffer);
        if Connection.FUtf8 then
           MyBuf := AnsiToUTF8(MyBuf);

        if Length(MyBuf)>0 then
           Move(MyBuf[1], (DestBuffer + GetFieldOffset(Field.FieldNo))^, Length(MyBuf)+1)
        else begin
           MyBuf := #0;
           Move(MyBuf[1], (DestBuffer + GetFieldOffset(Field.FieldNo))^, Length(MyBuf));
        end;
      End
    else
      Move(Buffer^, (DestBuffer + GetFieldOffset(Field.FieldNo))^, GetFieldSize(Field.FieldNo));
  end else begin
    if (State in [dsBrowse, dsEdit, dsInsert, dsCalcFields, dsBlockRead]) then begin
      if (Field.FieldNo < 0) and (Assigned(Buffer)) and (Assigned(DestBuffer)) then begin
        Move(Buffer^, (CalcBuffer + GetCalcFieldOffset(Field))^, GetFieldSize(Field));
      end;
    end;
  end;

  if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
    DataEvent(deFieldChange, Longint(Field));
end;

procedure TRaSQLiteBaseQuery.InternalFirst;
begin
  FCurRec := -1;
end;

procedure TRaSQLiteBaseQuery.InternalLast;
begin
  FCurRec := FResult.Count;
end;

function TRaSQLiteBaseQuery.GetLastInsertRow: integer;
begin
  if Assigned(Connection) then
    result := Connection.SQLite3_LastInsertRow(Connection.DBHandle)
  else
    result := -1;
end;

procedure TRaSQLiteBaseQuery.InternalPost;
var
  ptr               : Pointer;
begin
  FSaveChanges := true;
  if State = dsEdit then
  begin
    if FUniDir then
       Connection.SQLite3_GetNextResult(Connection.DBHandle, FStatement, FParams,self)
    else
       ptr := FResult.GetData(FCurrec);
    if ptr <> nil then
      move(ActiveBuffer^, ptr^, FRecBufSize);
  end
  else
  begin
    FResult.Insert(FCurRec, ActiveBuffer,
      Connection.SQLite3_LastInsertRow(Connection.DBHandle));
  end;
end;

procedure TRaSQLiteBaseQuery.InternalAddRecord(Buffer: Pointer; Append: boolean);
begin
  if FReadOnly then
     raise RaSqliteError.Create('Cannot write to a read-only dataset');

  FSaveChanges := true;
  if Append then
    InternalLast;
  Post;
end;

procedure TRaSQLiteBaseQuery.InternalDelete;
begin
  FSaveChanges := true;
  FResult.Delete(FCurRec);
  if FCurRec >= FResult.Count then
    Dec(FCurRec);
end;

function TRaSQLiteBaseQuery.GetRecordCount: longint;
begin
  Result := FResult.Count;
end;

function TRaSQLiteBaseQuery.GetRecNo: longint;
begin
  UpdateCursorPos;
  if (FCurRec = -1) and (RecordCount > 0) then
    Result := 1
  else
    Result := FCurRec + 1;
  NotifySQLiteMasterChanged;
end;

procedure TRaSQLiteBaseQuery.SetRecNo(Value: integer);
begin
  if (Value >= 0) and (Value < FResult.Count + 2) then
  begin
    FCurRec := Value - 1;
    Resync([]);
  end;
end;

procedure TRaSQLiteBaseQuery.SetFiltered(Value: Boolean);
begin
  inherited;
end;

procedure TRaSQLiteBaseQuery.SetFilterText(const Value: string);
begin
  Close;
  inherited;
end;

function TRaSQLiteBaseQuery.SetQueryParams(InStr: string): string;
var
  i                 : integer;
  TempParam         : string;
  ThisDateFormat    : string;
  OldDateFormat     : string;
begin
  if FSQLiteDateFormat then
    ThisDateFormat := 'yyyy-mm-dd hh:nn:ss.zzz'
  else if (FTableDateFormat <> '') then
    ThisDateFormat := FTableDateFormat
  else
    ThisDateFormat := ShortDateFormat;

  for i := 0 to FParams.Count - 1 do begin
    if (FParams.Items[i].DataType <> ftBlob) and
      (FParams.Items[i].DataType <> ftGraphic) then begin
      TempParam := Fparams.Items[i].AsString;
      if (TempParam = '') and (FParams.Items[i].bound) then begin
        InStr := StringReplace(Instr, '?', 'NULL', []);
      end else begin
        TempParam := StringReplace(TempParam, '?', #1, [rfReplaceAll]);
        if FParams[i].DataType = ftDate then begin
        end else if FParams[i].DataType = ftTime then begin
        end else if FParams[i].DataType = ftDateTime then begin
          OldDateFormat := ShortDateFormat;
          ShortDateFormat := ThisDateFormat;
          TempParam := DateToStr(FParams[i].AsDateTime);
          ShortDateFormat := OldDateFormat;
        end;

        InStr := StringReplace(Instr, '?', QuotedStr(TempParam), [rfIgnoreCase]);
      end;
    end else begin
      InStr := StringReplace(Instr, '?', #2, [rfIgnoreCase]);
    end;
  end;
  InStr := StringReplace(Instr, #1, '?', [rfReplaceAll]);
  InStr := StringReplace(InStr, ' = NULL', ' IS NULL', [rfReplaceAll]);

  SetQueryParams := InStr;
end;

constructor TRaSQLiteUpdateSQL.Create(AOWner: TComponent);
begin
  inherited Create(AOwner);
  FInsertSQL := TStringList.Create;
  FUpdateSQL := TStringList.Create;
  FDeleteSQL := TStringList.Create;
end;

destructor TRaSQLiteUpdateSQL.Destroy;
begin
  inherited;
  if Assigned(FInsertSQL) then
    FInsertSQL.Free;
  if Assigned(FUpdateSQL) then
    FUpdateSQL.Free;
  if Assigned(FDeleteSQL) then
    FDeleteSQL.Free;
end;

procedure TRaSQLiteUpdateSQL.SetInsertSQL(const Value: TStrings);
begin
  if Assigned(FInsertSQL) then
    FInsertSQL.Assign(Value)
  else
    FInsertSQL := Value;
end;

procedure TRaSQLiteUpdateSQL.SetUpdateSQL(const Value: TStrings);
begin
  if Assigned(FUpdateSQL) then
    FUpdateSQL.Assign(Value)
  else
    FUpdateSQL := Value;
end;

procedure TRaSQLiteUpdateSQL.SetDeleteSQL(const Value: TStrings);
begin
  if Assigned(FDeleteSQL) then
    FDeleteSQL.Assign(Value)
  else
    FDeleteSQL := Value;
end;

constructor TRaSQLiteQuery.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  TStringList(FSQL).OnChange := QueryChanged;
end;

destructor TRaSQLiteQuery.Destroy;
begin
  if Assigned(FSQL) then
    TStringList(FSQL).OnChange := nil;

  inherited Destroy;
end;

procedure TRaSQLiteQuery.Notification(AComponent: TComponent; Operation: TOperation);
begin
  if Assigned(AComponent) then
  begin
    if (Operation = opRemove) then begin
      if Assigned(FUpdateSQL) and (AComponent is TRaSQLiteUpdateSQL) then begin
        if TRaSQLiteUpdateSQL(AComponent) = FUpdateSQL then
          FUpdateSQL := nil;
      end else

        if Assigned(FConnection) then begin
          if (AComponent is TRaSQLiteDB) and
            (TRaSQLiteDB(AComponent) = FConnection) then begin
            Close;
            Connection := nil;
          end;
        end else

    end;
  end;
  inherited;
end;

procedure TRaSQLiteQuery.QueryChanged(Sender: TObject);
begin
  FNoResults := false;
  Close;
  if not FRawSQL then begin
    if assigned(FParams) then FParams.Clear;
    SQLStr := FParams.ParseSQL(SQL.Text, true)
  end else SQLStr := SQL.Text;
end;

procedure TRaSQLiteQuery.SetSQL(const Value: TStrings);
begin
  FNoResults := false;
  Close;
  if Assigned(FSQL) then
    FSQL.Assign(Value)
  else
    FSQL := Value;
end;

function TRaSQLiteQuery.GetSQL: TStrings;
begin
  GetSQL := FSQL;
end;

procedure TRaSQLiteQuery.InternalDelete;
var
  MySQL             : string;
  TempSQL           : string;
  TheWord           : string;
  TableId           : string;
  FieldId           : string;
  startpos          : integer;
  vartype           : integer;
  p                 : integer;
label
  Ende;
begin
  if FReadOnly then
    raise RaSqliteError.Create('Cannot delete from a read-only dataset');

  if Connection.FConnected then
  begin
    if FAutoCommit then
      Connection.ExecStartTransaction(FTransactionType);

    if not Assigned(FUpdateSQL) then 
    begin
      raise RaSqliteError.Create('Missing TRaSQLiteUpdateSQL component');
      goto ende;
    end;
    MySQL := FUpdateSQL.FDeleteSQL.Text;
    startpos := 1;

    TheWord := GetWord(MySQL, startpos, vartype);
    if not SyntaxCheck(TheWord, 'delete') then
      goto ende;

    TheWord := GetWord(MySQL, startpos, vartype);
    if not SyntaxCheck(TheWord, 'from') then
      goto ende;

    Tableid := GetWord(MySQL, startpos, vartype);

    TheWord := GetWord(MySQL, startpos, vartype);
    if not SyntaxCheck(TheWord, 'where') then
      goto ende;

    SQLStr := 'delete from ' + TableId + ' where ';
    TempSQL := Copy(MySQL, startpos, 999);

    p := pos(':', TempSQL);
    while p > 0 do
    begin
      SQLStr := SQLStr + Copy(TempSQL, 1, p - 1);
      System.Delete(TempSQL, 1, p);
      startpos := 1;
      FieldId := GetWord(TempSQL, startpos, vartype);
      System.Delete(TempSQL, 1, startpos);
      SQLStr := SQLStr + QuotedStr(FieldByName(FieldId).AsString); 
      p := pos(':', TempSQL);
    end;
    SQLStr := SQLStr + Copy(TempSQL, StartPos, 999);
    try
      Connection.SQLite3_execute(Connection.DBHandle, PAnsiChar(SQLStr), FParams, self);
      if FAutoCommit then
        Connection.Commit;
    except
      if FAutoCommit then begin
        Connection.RollBack;
        raise;
      end;
    end;
    inherited InternalDelete;
  end;
  Ende:
end;

procedure TRaSQLiteQuery.InternalPost;
var
  i                 : integer;
  p                 : integer;
  startpos          : integer;
  MyFieldList       : TStringList;
  MyFieldValues     : TStringList;
  MySQL             : string;
  TheWord           : string;
  TempSQL           : string;
  TableId           : string;
  FieldId           : string;
  varType           : integer;
begin
  if FReadOnly then
    raise RaSqliteError.Create('Cannot post into a read-only dataset');
  MyFieldList := nil;
  MyFieldValues := nil;
  try
    if not Connection.FConnected then
    begin
      exit;
    end;
    if FAutoCommit then
      Connection.ExecStartTransaction(FTransactionType);
    if not Assigned(FUpdateSQL) then
    begin
      raise RaSqliteError.Create('Missing TRaSQLiteUpdateSQL component');
    end;

    if (State = dsEdit) and (FResult.Count > 0) then
    begin
      MyFieldList := TStringList.Create;
      MyFieldValues := TStringList.Create;
      MySQL := FUpdateSQL.FUpdateSQL.Text;
      startpos := 1;
      TheWord := GetWord(MySQL, startpos, vartype);
      if not SyntaxCheck(TheWord, 'update') then
        exit;

      Tableid := GetWord(MySQL, startpos, vartype);

      TheWord := GetWord(MySQL, startpos, vartype);
      if TheWord = '*' then
      begin
        for i := 0 to FieldList.Count - 1 do 
        begin
          MyFieldList.Add(FieldList[i].FieldName);
          MyFieldValues.Add(':' + FieldList[i].FieldName);
        end;
        TheWord := GetWord(MySQL, startpos, vartype);
      end
      else
      begin
        if not SyntaxCheck(TheWord, 'set') then
        begin
          exit;
        end;

        repeat
          TheWord := GetWord(MySQL, startpos, vartype);
          MyFieldList.Add(TheWord);

          TheWord := GetWord(MySQL, startpos, vartype);
          if not SyntaxCheck(TheWord, '=') then
          begin
            exit;
          end;
          TheWord := GetWord(MySQL, startpos, vartype);
          if vartype = vtcDelimiter then
            TheWord := GetWord(MySQL, startpos, vartype);
          if TheWord = '*' then
            MyFieldValues.Add(':' + MyFieldList[MyFieldList.Count - 1])
          else
            MyFieldValues.Add(':' + TheWord);

          TheWord := GetWord(MySQL, startpos, vartype);
        until CompareText(TheWord, 'where') = 0;
      end;

      if not SyntaxCheck(TheWord, 'where') then
        exit;

      SQLStr := 'update ' + TableId + ' set ';
      for i := 0 to FieldList.Count - 1 do
      begin
        SQLStr := SQLStr + FieldList[i].FieldName + '=';
        FieldId := MyFieldValues[i];
        if FieldId[1] = ':' then
        begin
          System.Delete(FieldId, 1, 1);
          SQLStr := SQLStr + QuotedStr(FieldByName(FieldId).AsString) + ','
        end
        else
          SQLStr := SQLStr + QuotedStr(FieldId) + ','
      end;
      System.Delete(SQLStr, Length(SQLStr), 1);
      TempSQL := ' where ' + Copy(MySQL, startpos, 999);

      p := pos(':', TempSQL);
      while p > 0 do
      begin
        SQLStr := SQLStr + Copy(TempSQL, 1, p - 1);
        System.Delete(TempSQL, 1, p);
        startpos := 1;
        FieldId := GetWord(TempSQL, startpos, vartype);
        System.Delete(TempSQL, 1, startpos);
        SQLStr := SQLStr + QuotedStr(FieldByName(FieldId).AsString);
        p := pos(':', TempSQL);
      end;
      SQLStr := SQLStr + Copy(TempSQL, StartPos, 999);

      Connection.SQLite3_ExecSQL(SQLStr);
      inherited InternalPost;
    end
    else
    begin
      MyFieldList := TStringList.Create;
      MyFieldValues := TStringList.Create;
      MySQL := FUpdateSQL.FInsertSQL.Text;
      startpos := 1;
      TheWord := GetWord(MySQL, startpos, vartype);
      if not SyntaxCheck(TheWord, 'insert') then
        exit;

      TheWord := GetWord(MySQL, startpos, vartype);
      if not SyntaxCheck(TheWord, 'into') then
        exit;

      Tableid := GetWord(MySQL, startpos, vartype);

      TheWord := GetWord(MySQL, startpos, vartype);
      if TheWord = '*' then
      begin
        for i := 0 to FieldList.Count - 1 do
        begin
          MyFieldList.Add(FieldList[i].FieldName);
          MyFieldValues.Add(':' + FieldList[i].FieldName);
        end;
      end
      else if TheWord = '(' then
      begin
        repeat
          TheWord := GetWord(MySQL, startpos, vartype);
          MyFieldList.Add(TheWord);
          TheWord := GetWord(MySQL, startpos, vartype);
        until theword = ')';
        TheWord := GetWord(MySQL, startpos, vartype);
        TheWord := GetWord(MySQL, startpos, vartype);
        if TheWord = '*' then
        begin
          for i := 0 to MyFieldList.Count - 1 do
            MyFieldValues.Add(':' + MyFieldList[i]);
        end
        else
        begin
          repeat
            TheWord := GetWord(MySQL, startpos, vartype);
            if vartype = vtcDelimiter then begin
              TheWord := GetWord(MySQL, startpos, vartype);
              MyFieldValues.Add(':' + TheWord);
            end else
              MyFieldValues.Add(TheWord);
            TheWord := GetWord(MySQL, startpos, vartype);
          until theword = ')';
        end;
      end
      else
      begin
        raise RaSqliteError.Create('SQL macro syntax error on insertsql, expected ( or *');
      end;

      SQLStr := 'insert into ' + TableId + ' (';
      for i := 0 to MyFieldList.Count - 1 do
        SQLStr := SQLStr + MyFieldList[i] + ',';
      SQLStr[Length(SQLStr)] := ')';
      SQLStr := SQLStr + ' values (';
      for i := 0 to MyFieldList.Count - 1 do
      begin
        FieldId := MyFieldValues[i];
        if FieldId[1] = ':' then begin
          System.Delete(FieldId, 1, 1);
          SQLStr := SQLStr + QuotedStr(FieldByName(FieldId).AsString) + ','
        end else begin
          if CompareText(FieldId,'null')=0 then
             SQLStr := SQLStr + FieldId+','
          else
             SQLStr := SQLStr + QuotedStr(FieldId) + ','
        end;
      end;
      SQLStr[Length(SQLStr)] := ')';
      Connection.SQLite3_ExecSQL(SQLStr);
      if FResult.Count = 0 then
        Inc(FCurrec);
      inherited InternalPost;
    end;
    if FAutoCommit then
    begin
      try
        Connection.Commit;
      except
        Connection.RollBack;
        raise;

      end;
    end;
  finally;
    if Assigned(MyFieldList) then MyFieldList.Free;
    if Assigned(MyFieldValues) then MyFieldValues.Free;
  end;
end;

procedure TRaSQLiteQuery.InternalClose;
begin
  FPrepared := '';
  inherited;
end;

procedure TRaSQLiteQuery.InternalOpen;
begin
  if Trim(FSQL.Text) = '' then
  begin
    raise RaSqliteError.Create('no query specified');
    abort;
  end;

  if (FMaxResults = 0) and (FStartResult <> 0) then
    FMaxResults := -1;
  FPrepared := SQLStr;
  if (Filtered) and (Filter <> '') then
  begin
    FPrepared := 'select * from (' + FPrepared + ') where ' + Filter;
  end;

  if FParams.Count > 0 then
    FPrepared := SetQueryParams(FPrepared);

  if FMaxResults <> 0 then
    FPrepared := FPrepared + ' limit ' + IntToStr(FMaxResults);
  if FStartResult <> 0 then
    FPrepared := FPrepared + ' offset ' + IntToStr(FMaxResults);
  inherited;
end;

procedure TRaSQLiteQuery.SQLiteMasterChanged;
var
  r, s              : string;
  m, d              : string;
  p                 : integer;
  cAnd              : string;
begin
  Close;
  cAnd := '';
  r := FMasterFields;
  Filter := '';
  Filtered := false;
  while r <> '' do
  begin
    p := pos(';', r);
    if p = 0 then
    begin
      if Trim(r) <> '' then
        s := r;
      r := '';
    end
    else
    begin
      s := Trim(Copy(r, 1, p - 1));
      System.Delete(r, 1, p);
    end;

    p := pos('=', s);
    if p = 0 then
    begin
      raise RaSqliteError.Create('Syntax error: Masterfields not build of a=b;... pairs');
    end
    else
    begin
      d := copy(s, 1, p - 1);
      m := copy(s, p + 1, 99);
    end;
    Filter := Filter + cAnd + d + '=' + QuotedStr(FMasterSource.DataSet.FieldByName(m).AsString) ;
    cAnd := ' and ';
  end;
  if (Filter <> '') and (Active) then begin
    filtered := true;
    Open;
  end;
end;

procedure TRaSQLiteBaseQuery.ExecSQL;
begin
  FNoResults := true;
  Close;
  if FAutoCommit then
  begin
    Connection.ExecStartTransaction(FTransactionType);
    Open;
    try
      Connection.Commit
    except
      Connection.RollBack;
      raise;
    end;
  end
  else
    Open;
end;

procedure TRaSQLiteBaseQuery.SetParamsList(Value: TParams);
begin
  FParams.AssignValues(Value);
end;

function TRaSQLiteBaseQuery.GetParamsCount: word;
begin
  Result := FParams.Count;
end;

procedure TRaSQLiteTable.SetFTableName(TableName : string);
begin
  Close;
  FTableName := TableName;
end;

procedure TRaSQLiteTable.Notification(AComponent: TComponent; Operation: TOperation);
begin
  if Assigned(AComponent) then
  begin
    if (Operation = opRemove) then
    begin
      if (AComponent is TRaSQLiteDB) and Assigned(FConnection) then
      begin
        if TRaSQLiteDB(AComponent) = FConnection then begin
          Close;
          FConnection := nil;
        end;
      end else

    end;
  end;
  inherited;
end;

procedure TRaSQLiteTable.InternalOpen;
begin
  if FTableName = '' then
  begin
    raise RaSqliteError.Create('no table specified');
    exit;
  end;
  FSQL.Clear;
  FSQL.Add('select *, rowid as rowid from ' + TableName);
  if Filtered then
    if Filter <> '' then
      FSQL.Add(' where ' + Filter);
  if (FMaxResults = 0) and (FStartResult <> 0) then
    FMaxResults := -1;
  if FMaxResults <> 0 then
    FSQL.Add(' limit ' + IntToStr(FMaxResults));
  if FStartResult <> 0 then
    FSQL.Add(' offset ' + IntToStr(FStartResult));
  SQLStr := FSQL.Text;
  FPrepared := SQLStr;
  inherited;
end;

procedure TRaSQLiteTable.SQLiteMasterChanged; 
var
  r, s              : string;
  m, d              : string;
  p                 : integer;
  cAnd              : string;
begin
  Close;
  cAnd := '';
  r := FMasterFields;
  Filter := '';
  while r <> '' do
  begin
    p := pos(';', r);
    if p = 0 then
    begin
      if Trim(r) <> '' then
        s := r;
      r := '';
    end
    else
    begin
      s := Trim(Copy(r, 1, p - 1));
      System.Delete(r, 1, p);
    end;

    p := pos('=', s);
    if p = 0 then
    begin
      raise RaSqliteError.Create('Syntax error: Masterfields not build of a=b;... pairs');
    end
    else
    begin
      d := copy(s, 1, p - 1);
      m := copy(s, p + 1, 99);
    end;
    Filter := Filter + cAnd + d + '=' + QuotedStr(FMasterSource.DataSet.FieldByName(m).AsString) ;
    cAnd := ' and ';
  end;
  if Filter <> '' then
    filtered := true;
  Open;
end;

procedure TRaSQLiteTable.InternalDelete;
begin
  if FReadOnly then
    raise RaSqliteError.Create('Cannot delete from a read-only dataset');

  if not Connection.FConnected then
    exit;
  if FAutoCommit then
    Connection.ExecStartTransaction(FTransactionType);

  SQLStr := '';
  CurrentRowId := FResult.GetRowId(FCurRec);
  FSQL.Clear;
  FSQL.Add('delete from ' + Tablename + ' where rowid=' + QuotedStr(IntToStr(CurrentRowId)));
  SQLStr := FSQL.Text;
  Connection.SQLite3_execute(Connection.DBHandle, PAnsiChar(SQLStr), FParams, self);

  inherited InternalDelete;

  if FAutoCommit then
  begin
    try
      Connection.Commit;
    except
      Connection.RollBack; 
      raise;
    end;
  end;
end;

procedure TRaSQLiteTable.InternalPost;
var
  i : integer;
  n: Integer;
  ThisDateFormat,
  tmpMasterDetail, MasterField, chDelim: string;
  slDetail, slValues: TStringList;
  lsBlobs: TList;
  M:TMemoryStream;

  function GetFieldValue(const AField: TField): string;
  begin
    if AField.DataType = ftDateTime then
      GetFieldValue := QuotedStr(FormatDateTime(ThisDateFormat, FieldByName(AField.FieldName).AsDateTime))
    else if (AField.DataType = ftBlob) or (AField.DataType = ftMemo) or (AField.DataType = ftFmtMemo) or (AField.DataType = ftGraphic) then
      begin
        M:=TMemoryStream.Create;
        TBlobField(FieldByName(AField.FieldName)).SaveToStream(M);
        GetFieldValue := #2+IntToStr(1+lsBlobs.Add(Pointer(M)))
      end
    else
      GetFieldValue := QuotedStr(FieldByName(AField.FieldName).AsString);
  end;

var
  f: TField;
  OldDecimalSeparator: ansiChar;
begin
  if FReadOnly then
    raise RaSqliteError.Create('Cannot post into a read-only dataset');

  if FSQLiteDateFormat then
    ThisDateFormat := 'yyyy-mm-dd hh:nn:ss.zzz'
  else if (FTableDateFormat <> '') then
    ThisDateFormat := FTableDateFormat
  else
    ThisDateFormat := ShortDateFormat;

  if not Connection.FConnected then Exit;
  if FAutoCommit then Connection.StartTransaction;

  lsBlobs := TList.Create;
  OldDecimalSeparator := DecimalSeparator;
  try
    DecimalSeparator := '.';

    if (State = dsEdit) and (FResult.Count > 0) then
      begin
        CurrentRowId := FResult.GetRowId(FCurRec);
        FSQL.Clear;
        FSQL.Add('update ' + TableName + ' set ');
        SQLStr := '';
        for i := 0 to FieldList.Count - 1 do begin
            f := FieldList[i];
            if not (f.Calculated or f.Lookup) then
              SQLStr := SQLStr + f.FieldName + '=' + GetFieldValue(f) + ',';
          end;
        SQLStr[Length(SQLStr)] := ' ';
        FSQL.Add(SQLStr);
        FSQL.Add(' where rowid=' + QuotedStr(IntToStr(CurrentRowId)));

        SQLStr := FSQL.Text;

        Connection.SQLite3_ExecSQL(SQLStr,lsBlobs);
        inherited InternalPost;
      end
    else
      begin
        FSQL.Clear;
        FSQL.Add('insert into ' + TableName + ' (');
        SQLStr := '';

        for i := 0 to FieldList.Count - 1 do begin
            if not (FieldList[i].Calculated or FieldList[i].Lookup) then
              SQLStr := SQLStr + FieldList[i].FieldName + ',';
          end;

        SQLStr[Length(SQLStr)] := ')';
        SQLStr := SQLStr + ' values (';
        FSQL.Add(SQLStr);
        SQLStr := '';

        slDetail := TStringList.Create;
        slValues := TStringList.Create;
        i := 0; chDelim := ';';
        if FMasterSource <> nil then
          begin
            while i < Length(FMasterFields) do
              begin
                tmpMasterDetail := GetWordByDelim(FMasterFields, i, chDelim);
                n := Pos('=', tmpMasterDetail);
                if n <> 0 then
                  begin
                    slDetail.Add(Copy(tmpMasterDetail, 1, n - 1));
                    MasterField := Copy(tmpMasterDetail, n + 1, Length(tmpMasterDetail) - n);
                    slValues.Add(FMasterSource.DataSet.FieldByName(MasterField).AsString);
                  end;
              end;
          end;

        if (FPrimaryAutoInc) and (FieldDefs[0].DataType = ftInteger) then begin
          SQLStr := SQLStr + 'null,';
          n := 1;
        end else begin
          n := 0;
        end;

        for i := n to FieldList.Count - 1 do
          begin
            f := FieldList[i];
            if not (f.Calculated or f.Lookup) then
              if slDetail.Find(f.FieldName, n) then
                SQLStr := SQLStr + QuotedStr(slValues.Strings[n]) + ','
              else
                SQLStr := SQLStr + GetFieldValue(f) + ',';
          end;

        slDetail.Free;
        slValues.Free;

        SQLStr[Length(SQLStr)] := ')';
        FSQL.Add(SQLStr);

        SQLStr := FSQL.Text;
        Connection.SQLite3_ExecSQL(SQLStr, lsBlobs);
        if FPrimaryAutoInc then
          if FieldDefs[0].DataType = ftInteger then
            FieldByName(FieldList[0].FieldName).AsInteger :=
              Connection.SQLite3_LastInsertRow(Connection.DBHandle);
        if FResult.Count = 0 then
          Inc(FCurRec);
        inherited InternalPost;
      end;

  finally
    DecimalSeparator := OldDecimalSeparator;
    For I:=0 to lsBlobs.Count-1 do begin
      M:=TMemoryStream(lsBlobs.Items[I]);
      M.Free;
    end;
    lsBlobs.Free;

  end;

  if FAutoCommit then
    begin
      try
        Connection.Commit;
      except
        Connection.RollBack;
        raise;
      end;
    end;
end;

constructor TRaSQLiteBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
begin
  FField := Field;
  FMode := Mode;
  FDataSet := FField.DataSet as TRaSQLiteBaseQuery;
  if Mode <> bmWrite then
    LoadBlobData;
end;

destructor TRaSQLiteBlobStream.Destroy;
begin
  if FModified then
    SaveBlobData;
  inherited Destroy;
end;

function TRaSQLiteBlobStream.Read(var Buffer; Count: Longint): Longint;
begin
  Result := inherited Read(Buffer, Count);
  FOpened := True;
end;

function TRaSQLiteBlobStream.Write(const Buffer; Count: Longint): Longint;
begin
  Result := inherited Write(Buffer, Count);
  FModified := True;
  FDataSet.SetModified(true);
end;

procedure TRaSQLiteBlobStream.LoadBlobData;
var
  Stream            : TMemoryStream;
  Offset            : Integer;
  RecBuffer         : PAnsiChar;
begin
  Self.Size := 0;
  FDataset.GetActiveBuffer(RecBuffer);
  if RecBuffer <> nil then
  begin
    Offset := FDataset.GetFieldOffset(FField.FieldNo);
    Move((RecBuffer + Offset)^, Pointer(Stream), sizeof(Pointer));
    Self.CopyFrom(Stream, 0);
  end;
  Position := 0;
end;

procedure TRaSQLiteBlobStream.SaveBlobData;
var
  Stream            : TMemoryStream;
  Offset            : Integer;
  RecBuffer         : PAnsiChar;
begin
  FDataset.GetActiveBuffer(RecBuffer);
  if RecBuffer <> nil then
  begin
    Offset := FDataset.GetFieldOffset(FField.FieldNo);
    Move((RecBuffer + Offset)^, Pointer(Stream), sizeof(Pointer));
    Stream.Size := 0;
    Stream.CopyFrom(Self, 0);
    Stream.Position := 0; 
  end;
end;

constructor TRaSQLiteInlineSQL.Create;
begin
  inherited;
  FSQL := TStringList.Create;
end;

destructor TRaSQLiteInlineSQL.Destroy;
begin
  if Assigned(FSQL) then FSQL.Free;
  inherited;
end;

procedure TRaSQLiteInlineSQL.SetSQL(const Value: TStrings);
begin
  if Assigned(FSQL) then
    FSQL.Assign(Value)
  else
    FSQL := Value;
end;

function TRaSQLiteInlineSQL.GetSQL: TStrings;
begin
  GetSQL := FSQL;
end;

constructor TRaSQLiteOutput.Create;
begin
  inherited;
  FOutput := TStringList.Create;
end;

destructor TRaSQLiteOutput.Destroy;
begin
  if Assigned(FOutput) then FOutput.Free;
  inherited;
end;

procedure TRaSQLiteOutput.SetFActive(Active: boolean);
begin
  FActive := Active;
  if FActive = false then begin
  end else begin
    if Assigned(FDataSource) then begin
      if Assigned(FDataSource.DataSet) then begin
        Execute(FDataSource.DataSet);
      end else raise RaSqliteError.Create('Missing Datasource.Dataset'); 
    end else raise RaSqliteError.Create('Missing Datasource');
  end;
end;

procedure TRaSQLiteOutput.SetOutput(const Value: TStrings);
begin
  if Assigned(FOutput) then
    FOutput.Assign(Value)
  else
    FOutput := Value;
end;

function TRaSQLiteOutput.GetOutput: TStrings;
begin
  GetOutput := FOutput;
end;

procedure TRaSQLiteOutput.Notification(AComponent: TComponent; Operation: TOperation);
begin
  if Assigned(AComponent) then begin
    if (Operation = opRemove) then begin
      if (AComponent is TDataSource) then begin
        if Assigned(FDataSource) then begin
          if TDataSource(AComponent) = FDataSource then
            FDataSource := nil;
        end;
      end
    end;
  end;
  inherited;
end;

procedure TRaSQLiteOutput.Execute(MyDataSet: TDataSet);
const
  eXML          = 0;
  eHTML             = 1;
  eTXT              = 2;
var FType           : integer;
  i                 : integer;
  Line              : string;
  Sep               : string;
begin
  if Assigned(MyDataset) then begin
    if MyDataSet.Active = false then MyDataSet.Open;
    Output.Clear;
    FType := ETxt;
    Line := '';

    if CompareText(FOutputType[1], 'X') = 0 then begin
      FType := eXML;
      Line := Line + '<table id="' + MyDataSet.Name + '" CreationDate="' + DateToStr(Now) + '">' + #10;
    end else if CompareText(FOutputType[1], 'H') = 0 then begin
      FType := eHTML;
      Line := Line + '<html>' + #10 + '<head>' + #10 +
        '<title>Table ' + MyDataSet.Name + '</title>' + #10 +
        '<meta name="generator" content="Aducom Software ASQLite">' + #10 +
        '<table class="' + FTableClass + '"cellpadding="0" cellspacing="0">' + #10 +
        '<tr>' + #10;
    end else if CompareText(FOutputType[1], 'T') = 0 then begin
      FType := eTXT;
    end;

    Sep := '';

    for i := 0 to MyDataSet.FieldDefs.Count - 1 do begin
      case FType of
        eXML: begin
          end;
        eHTML: begin
            Line := Line + '<td>' + MyDataSet.FieldDefs[i].Name + '</td>';
          end;
        eTXT: begin
            Line := Line + Sep + MyDataSet.FieldDefs[i].Name;
          end;
      end;
      Sep := FSeparator;
    end;

    Output.Add(Line); Line := ''; Sep := '';
    MyDataSet.First;

    while not MyDataSet.Eof do begin

      case FType of
        eXML: Line := Line + '   <record>' + #10;
        eHTML: Line := Line + '<tr>' + #10;
      end;

      for i := 0 to MyDataSet.FieldDefs.Count - 1 do begin
        case FType of
          eXML: begin
              Line := Line + '      <' + MyDataSet.FieldDefs[i].Name + '>' +
                MyDataSet.FieldByName(MyDataSet.FieldDefs[i].Name).AsString +
                '</' + MyDataSet.FieldDefs[i].Name + '>' + #10;
            end;
          eHTML: begin
              Line := Line + '<td>' + MyDataSet.FieldByName(MyDataSet.FieldDefs[i].Name).AsString + '</td>';
            end;
          eTXT: begin
              Line := Line + Sep + MyDataSet.FieldByName(MyDataSet.FieldDefs[i].Name).AsString;
            end;
        end;
        Sep := FSeparator;
      end;

      case FType of
        eXML: Line := Line + '   </record>' + #10;
        eHTML: Line := Line + '</tr>' + #10;
      end;

      Output.Add(Line); Line := ''; Sep := '';
      MyDataSet.Next;
    end;

    case FType of
      eXML: Line := Line + '   </table>' + #10;
      eHTML: Line := Line + '</table>' + #10 + '</body>' + #10 + '</html>' + #10;
    end;
    Output.Add(Line);
  end;
end;

procedure TRaSQLiteBaseQuery.PSEndTransaction(Commit: Boolean);
begin
  if Assigned(Connection) then
    if Commit then
      Connection.Commit
    else
      Connection.RollBack;
end;

procedure TRaSQLiteBaseQuery.PSExecute;
begin
  UniDirectional := true;
  ExecSQL;
end;

function TRaSQLiteBaseQuery.PSExecuteStatement(const ASQL: string;
  AParams: TParams; ResultSet: Pointer): Integer;
var
  AsqlQry           : TRaSQLiteBaseQuery;
begin
  if Assigned(ResultSet) then
  begin
    TDataSet(ResultSet^) := TRaSQliteQuery.Create(nil);
    TRaSQliteQuery(ResultSet^).SetUniDirectional(true);
    with TRaSQLiteBaseQuery(ResultSet^) do begin
      Connection := self.Connection;
      Params.Assign(AParams);
      FPrepared := ASql;
      if FParams.Count > 0 then
        FPrepared := SetQueryParams(FPrepared);
      Open;
      Result := Connection.RowsAffected;
    end;
  end
  else
  begin
    AsqlQry := TRaSQLiteBaseQuery.Create(nil);
    try
      with AsqlQry do begin 
        Connection := self.Connection;
        FPrepared := ASql;
        FParams.Assign(AParams);
        if FParams.Count > 0 then
          FPrepared := SetQueryParams(AsqlQry.FPrepared);
        ExecSQL;
        Result := Connection.RowsAffected;
      end;
    finally
      AsqlQry.Free;
    end;
  end;
end;

function TRaSQLiteBaseQuery.PSGetParams: TParams;
begin
  Result := Params;
end;

function TRaSQLiteBaseQuery.PSGetQuoteChar: string;
begin
  Result := '"';
end;

function TRaSQLiteBaseQuery.PSGetTableName: string;
begin
  Result := GetTableNameFromSQL(FSQL.Text);
end;

function TRaSQLiteBaseQuery.PSInTransaction: Boolean;
begin
  Result := Assigned(Connection);
end;

function TRaSQLiteBaseQuery.PSIsSQLBased: Boolean;
begin
  Result := True;
end;

function TRaSQLiteBaseQuery.PSIsSQLSupported: Boolean;
begin
  Result := True;
end;

procedure TRaSQLiteBaseQuery.PSSetCommandText(const CommandText: string);
begin
  if CommandText <> '' then begin
    FSQL.Text := CommandText;
    FPrepared := CommandText;
  end;
end;

procedure TRaSQLiteBaseQuery.PSSetParams(AParams: TParams);
begin
  if AParams.Count <> 0 then
    Params.Assign(AParams);
  Close;
end;

procedure TRaSQLiteBaseQuery.PSStartTransaction;
begin
  StartTransaction;
end;

procedure TRaSQLiteBaseQuery.PSReset;
begin
  if Active then
  begin
    Close;
    Open;
  end;
end;

function TRaSQLiteBaseQuery.PSGetUpdateException(e: Exception; Prev: EUpdateError): EUpdateError;
var
  PrevErr           : Integer;
begin
  if e is RaSqliteError then begin
    if Prev = nil then
      PrevErr := Prev.errorCode
    else
      PrevErr := 0;
    with RaSqliteError(e) do
      raise EUpdateError.Create(e.Message, '', -1, PrevErr, e);
  end else
    raise EUpdateError.Create(e.Message, '', -1, -1, e);
end;

function TRaSQLiteBaseQuery.PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean;
begin
  Result := False;
end;

//procedure TRaSQLiteBaseQuery.PSGetAttributes(List: TList);
//begin
//
//end;

//function TRaSQLiteBaseQuery.PSGetDefaultOrder: TIndexDef;
//begin
//
//end;

//function TRaSQLiteBaseQuery.PSGetIndexDefs(IndexTypes: TIndexOptions): TIndexDefs;
//begin
//
//end;

function TRaSQLiteBaseQuery.PSGetKeyFields: string;
var
  i                 : integer;
begin
  Result := '';
  for i := 0 to (Fields.Count - 1) do begin
    if pfInKey in Fields[i].ProviderFlags then begin
      if Result <> '' then
        Result := Result + ';';
      Result := Result + Fields[i].FieldName;
    end;
  end;
end;

function TRaSQLiteDB.ChangePassword(AOldPwd, ANewPwd: string): boolean;
begin
  if SQLite3_Key(DBHandle, PAnsiChar(AOldPwd), Length(AOldPwd)) <> SQLITE_OK then
    Result := False
  else
    Result := SQLite3_Rekey(DBHandle, PAnsiChar(ANewPwd), Length(ANewPwd)) = SQLITE_OK;
end;

function TRaSQLiteDB.CheckPassword: Boolean;
begin
  if FPassword <> '' then
  try
    result := (SQLite3_Key(DBHandle, PAnsiChar(FPassword), Length(FPassword)) = SQLITE_OK)
  except
    Result := false;
  end
  else
    Result := True;
end;

end.
